Summary
Execution plan for the GoodDaoHouses.sol MVP requested in #297.
Execution plan
1. Contract placement and architecture
- Add
GoodDaoHouses.sol under contracts/governance/ next to CompoundVotingMachine.sol, GReputation.sol, and the staking contracts.
- Keep the MVP as one contract with internal structs/enums for house membership, HoA eligibility, vote snapshots, ballots, finalized results, and FlowSplitter pool tracking.
- Make the contract upgradeable and NameService-aware so it fits the existing governance deployment model.
2. Existing repo patterns to reuse
- Reuse
DAOUpgradeableContract + NameService lookup for protocol wiring and avatar-authorized upgrades.
- Reuse OpenZeppelin
AccessControlUpgradeable role patterns already used in Reputation.sol and Faucet.sol for committee/admin permissions.
- Reuse snapshot-style governance ideas from
CompoundVotingMachine.sol: explicit vote creation, stored start/end boundaries, and frozen voting eligibility at creation time.
- Reuse staking UX from
GoodDollarStaking.sol, especially the onTokenTransfer entrypoint for one-transaction staking.
3. Token and single-transaction staking path
- Use the G$ token already exposed through NameService (
GOODDOLLAR) and its existing transferAndCall / ERC677-style callback path.
- Preferred registration flow:
GoodDollar.transferAndCall(housesContract, amount, encodedRegistrationData) -> onTokenTransfer decodes house type + metadata and performs register-and-stake atomically.
- Keep a plain
registerAndStake / stake path only if needed for operational flexibility, but the primary MVP path should be ERC677-based because it already exists and is tested in this repo.
- Do not base the MVP on ERC777 hooks; the repo’s proven staking pattern for G$ membership flows is
transferAndCall.
4. Membership and registry model
- Define
enum House { Citizens, Alignment } and enum MemberStatus { None, Pending, Active, Revoked, Unstaked }.
- Store per-member state: house, status, staked amount, joinedAt, updatedAt, unstakedAt, and metadata fields required by each house.
- Store separate HoA eligibility records with
isEligible, listedAt, updatedAt, and optional delisted timestamp.
- Restrict HoA registration to eligible addresses only; HoC registration can activate immediately after staking.
- Preserve historical participation through events and timestamps even after revocation or unstake.
5. Permission model
- Use
DEFAULT_ADMIN_ROLE for emergency/admin actions such as pausing and role management.
- Use one broad
GOVERNANCE_COMMITTEE_ROLE in the MVP for HoA eligibility management, HoA approval/revocation, vote creation, vote finalization, and result execution.
- Defer finer-grained role splitting unless a reviewer explicitly wants operational separation; one committee role matches the stated MVP scope and keeps deployment simpler.
- Add pause protection only on sensitive write paths: registration, approval/revocation, vote creation, vote casting updates, finalize, and execute.
6. AlignmentVote shape
- Model a single
AlignmentVote type with manual creation by committee for the quarterly cycle.
- On creation, snapshot:
- active HoA recipients
- active HoA voters
- active HoC voters
- fixed per-house voting weights (
40 HoA / 4 HoC)
- vote open/close timestamps (default 7 days)
- Store per-voter ballot totals so a voter can replace their allocation while the vote is open.
- Finalization should compute and persist canonical results on-chain before any FlowSplitter call.
- Execution must be a separate permissioned step, one-time only, and must not erase finalized results if downstream execution fails.
7. Read/write/event surface to plan for
- Reads: member records, HoA eligibility, stake requirement config, active-member checks, vote config, vote snapshots, ballot state, finalized allocations, execution status, FlowSplitter pool config, FlowSplitter pool id, and FlowSplitter pool address.
- Writes: add/remove HoA eligibility, register via callback, approve/revoke HoA member, unstake, create vote, cast/update vote, finalize vote, execute results, configure FlowSplitter, create the FlowSplitter pool, sync pool metadata, pause/unpause.
- Events: eligibility added/removed, member registered, member approved, member revoked, unstaked, vote created, vote updated, vote finalized, vote executed, FlowSplitter config changed, FlowSplitter pool created.
8. FlowSplitter integration boundary
- Use the published
IFlowSplitter contract shape from Flowstate docs/Git source, not an ad-hoc local interface.
- The exact contract support needed is:
createPool(ISuperToken, PoolConfig, PoolERC20Metadata, Member[], address[], string) returns (ISuperfluidPool)
updateMembersUnits(uint256, Member[])
updatePoolMetadata(uint256, string)
isPoolAdmin(uint256, address)
getPoolById(uint256) for verification/recovery reads
- optional admin sync through
addPoolAdmin, removePoolAdmin, or updatePoolAdmins if operational ownership needs to change later
- Mirror the external data types exactly when integrating:
Member { address account; uint128 units; }
Admin { address account; AdminStatus status; }
Pool { uint256 id; address poolAddress; address token; string metadata; bytes32 adminRole; }
PoolConfig { bool transferabilityForUnitsOwner; bool distributionFromAnyAddress; }
PoolERC20Metadata { string name; string symbol; uint8 decimals; }
- The integration must persist enough state to keep updating the same pool after creation: FlowSplitter contract address, pool id, pool address, metadata, and whether the pool has already been initialized.
- Because FlowSplitter admin permissions are address-based,
GoodDaoHouses itself should be registered as a pool admin if it is the contract that will perform future updateMembersUnits calls; GovCo authorization should stay enforced inside GoodDaoHouses rather than by making rotating EOAs the direct FlowSplitter admins.
- Pool creation should use the GoodDollar SuperToken and custom pool metadata. The contract should create the pool once on first successful execution, then reuse
updateMembersUnits on subsequent executions rather than recreating the pool.
- The finalized vote output has to be converted into FlowSplitter
uint128 units. Since allocations are defined as votePercentage * totalUnits, the implementation should normalize every recipient into deterministic units against totalUnits before calling createPool or updateMembersUnits.
- Unstaking or approval removal should send an explicit
Member(account, 0) unit update so the recipient’s incoming stream allocation is cleared.
- Metadata should remain plain strings and be propagated through
createPool(..., _metadata) and updatePoolMetadata as needed.
- Recommended pool configuration for the governance-controlled MVP is
transferabilityForUnitsOwner = false and distributionFromAnyAddress = false, so unit ownership and stream distribution stay under governance-controlled execution.
9. Deployment and test conventions
- Follow the repo’s upgradeable deployment pattern used by governance contracts and multichain deploy scripts.
- Register the deployed contract through
NameService / release metadata like the existing governance components.
- Add governance tests under
test/governance/ using createDAO() fixtures and loadFixture(...).
- Cover at minimum:
- HoA eligibility gating
- ERC677 register+stake flow
- HoC immediate activation
- HoA pending -> active approval flow
- revocation / unstake state transitions
- vote snapshot correctness
- weighted vote replacement logic
- finalize-before-execute guarantee
- single-execution and failed-execution persistence
- FlowSplitter pool creation on first execution
- FlowSplitter member unit updates on re-execution
- zero-unit update on unstake / HoA approval removal
- role and pause enforcement
10. Resolved implementation requirements
- Minimum stake amounts should be configurable per house by the GovCo role.
- Unstaking should fully clear HoA approval and update units so the affected recipient’s incoming stream is set to
0.
- Metadata should be stored as plain strings on-chain for now.
- FlowSplitter execution should use the GoodDollar SuperToken, with allocations calculated as
votePercentage * totalUnits.
Summary
Execution plan for the
GoodDaoHouses.solMVP requested in #297.Execution plan
1. Contract placement and architecture
GoodDaoHouses.solundercontracts/governance/next toCompoundVotingMachine.sol,GReputation.sol, and the staking contracts.2. Existing repo patterns to reuse
DAOUpgradeableContract+NameServicelookup for protocol wiring and avatar-authorized upgrades.AccessControlUpgradeablerole patterns already used inReputation.solandFaucet.solfor committee/admin permissions.CompoundVotingMachine.sol: explicit vote creation, stored start/end boundaries, and frozen voting eligibility at creation time.GoodDollarStaking.sol, especially theonTokenTransferentrypoint for one-transaction staking.3. Token and single-transaction staking path
GOODDOLLAR) and its existingtransferAndCall/ ERC677-style callback path.GoodDollar.transferAndCall(housesContract, amount, encodedRegistrationData)->onTokenTransferdecodes house type + metadata and performs register-and-stake atomically.registerAndStake/stakepath only if needed for operational flexibility, but the primary MVP path should be ERC677-based because it already exists and is tested in this repo.transferAndCall.4. Membership and registry model
enum House { Citizens, Alignment }andenum MemberStatus { None, Pending, Active, Revoked, Unstaked }.isEligible,listedAt,updatedAt, and optional delisted timestamp.5. Permission model
DEFAULT_ADMIN_ROLEfor emergency/admin actions such as pausing and role management.GOVERNANCE_COMMITTEE_ROLEin the MVP for HoA eligibility management, HoA approval/revocation, vote creation, vote finalization, and result execution.6. AlignmentVote shape
AlignmentVotetype with manual creation by committee for the quarterly cycle.40HoA /4HoC)7. Read/write/event surface to plan for
8. FlowSplitter integration boundary
IFlowSplittercontract shape from Flowstate docs/Git source, not an ad-hoc local interface.createPool(ISuperToken, PoolConfig, PoolERC20Metadata, Member[], address[], string) returns (ISuperfluidPool)updateMembersUnits(uint256, Member[])updatePoolMetadata(uint256, string)isPoolAdmin(uint256, address)getPoolById(uint256)for verification/recovery readsaddPoolAdmin,removePoolAdmin, orupdatePoolAdminsif operational ownership needs to change laterMember { address account; uint128 units; }Admin { address account; AdminStatus status; }Pool { uint256 id; address poolAddress; address token; string metadata; bytes32 adminRole; }PoolConfig { bool transferabilityForUnitsOwner; bool distributionFromAnyAddress; }PoolERC20Metadata { string name; string symbol; uint8 decimals; }GoodDaoHousesitself should be registered as a pool admin if it is the contract that will perform futureupdateMembersUnitscalls; GovCo authorization should stay enforced insideGoodDaoHousesrather than by making rotating EOAs the direct FlowSplitter admins.updateMembersUnitson subsequent executions rather than recreating the pool.uint128units. Since allocations are defined asvotePercentage * totalUnits, the implementation should normalize every recipient into deterministic units againsttotalUnitsbefore callingcreatePoolorupdateMembersUnits.Member(account, 0)unit update so the recipient’s incoming stream allocation is cleared.createPool(..., _metadata)andupdatePoolMetadataas needed.transferabilityForUnitsOwner = falseanddistributionFromAnyAddress = false, so unit ownership and stream distribution stay under governance-controlled execution.9. Deployment and test conventions
NameService/ release metadata like the existing governance components.test/governance/usingcreateDAO()fixtures andloadFixture(...).10. Resolved implementation requirements
0.votePercentage * totalUnits.