Skip to content

feat: Updatable Proposals & Signature-based Sponsorship (Governor v3.0.0)#5

Open
dan13ram wants to merge 65 commits into
mainfrom
feat/updatable-proposals
Open

feat: Updatable Proposals & Signature-based Sponsorship (Governor v3.0.0)#5
dan13ram wants to merge 65 commits into
mainfrom
feat/updatable-proposals

Conversation

@dan13ram

@dan13ram dan13ram commented Apr 21, 2026

Copy link
Copy Markdown
Collaborator

This PR introduces updatable proposals and signature-based sponsorship to the Governor contract, enabling collaborative proposal creation and iterative refinement during a designated editing window. This is a major version bump to v3.0.0 with significant protocol upgrades.

Summary

  • Updatable Proposals: New 3-phase proposal lifecycle with an editable window (Updatable → Pending → Active)
  • Signature-based Sponsorship: Enable proposals backed by multiple signers through proposeBySigs and updateProposalBySigs
  • Deterministic Deployments: Manager now supports CREATE2-based deterministic DAO deployments
  • New Metadata Renderers: Added PropertyIPFS and MerklePropertyIPFS metadata renderers
  • Comprehensive Testing: 1800+ new test lines including fork tests for mainnet upgrades
  • Upgraded Dependencies: OpenZeppelin v5.6, Solidity 0.8.35, via_ir compilation

Key Features

1. Updatable Proposals

  • Proposals start in an Updatable state for a configurable period (default: 1 day, max: 24 weeks)
  • Original proposer can update transaction targets, values, calldata, and description during this window
  • Voting power snapshot is frozen at original creation time to prevent gaming
  • Updates create a replacement chain with explicit proposalIdReplacedBy mapping

2. Signature-based Sponsorship

  • New proposeBySigs() function allows proposals backed by multiple signers (max 16)
  • Combined voting power of proposer + signers must exceed proposal threshold
  • EIP-712 signatures with nonce + deadline replay protection
  • Full ERC-1271 compatibility for smart contract wallets
  • Flexible signer sets on updates (can add, remove, or replace signers)

3. Breaking Changes

  • CRITICAL: castVoteBySig ABI changed from (v,r,s) to (nonce, deadline, bytes sig)
  • Proposal IDs now bytes32 (hash-based) instead of sequential integers
  • New typehashes: PROPOSAL_TYPEHASH and UPDATE_PROPOSAL_TYPEHASH

4. Manager Enhancements

  • deployDeterministic() for CREATE2-based predictable DAO addresses
  • predictDeterministicAddresses() for address prediction before deployment
  • Explicit implementation parameters for versioned deployments
  • Backward-compatible legacy deploy() function retained

5. New Metadata Renderers

  • PropertyIPFS: IPFS-backed property metadata renderer with trait generation
  • MerklePropertyIPFS: Merkle-proof based property renderer for gas-efficient large trait sets
  • Shared BaseMetadata contract for common functionality

Technical Improvements

  • Solidity 0.8.35: Upgraded from 0.8.16
  • via_ir compilation: Enabled for better optimization and gas savings
  • OpenZeppelin v5.6: Upgraded from v4.x (removed -upgradeable variants)
  • Reduced max signers: 32 → 16 for gas savings (src/governance/governor/Governor.sol:72)
  • Storage layout validation: Automated storage layout checks in CI
  • Comprehensive linting: Updated solhint rules and prettier config

Testing

  • 1920 lines of new Governor tests including fuzz tests and gas benchmarks
  • 768 lines of Purple DAO system upgrade fork tests
  • 330 lines of mainnet Manager upgrade fork tests
  • Full lifecycle testing for proposal updates, signature verification, and state transitions
  • Gas benchmarking suite for proposal operations

Documentation

  • docs/governor-architecture.md - Hybrid EAS + onchain signature architecture
  • docs/governor-proposal-lifecycle.md - Complete state machine reference
  • docs/governor-audit-readiness.md - Production readiness checklist
  • docs/frontend-migration-guide.md - Breaking changes and migration guide (562 lines)
  • docs/frontend-subgraph-integration-guide.md - Subgraph indexing guide (2020 lines)
  • docs/upgrade-runbook.md - Step-by-step upgrade procedure
  • docs/eas-proposal-candidates-schema.md - EAS schema reference (2100 lines)

Deployment

  • ✅ Deployed v3.0.0 to Ethereum Sepolia, Base Sepolia, and Optimism Sepolia
  • Deploy scripts: script/DeployV3New.s.sol and script/DeployV3Upgrade.s.sol
  • Merkle property renderer deployed to all supported chains

Migration Notes

For Existing DAOs:

  1. Upgrade Governor implementation via governance vote
  2. Frontend must update to new castVoteBySig ABI before upgrade
  3. Set proposalUpdatablePeriod via updateProposalUpdatablePeriod()
  4. No automatic data migration needed (storage append-only)

For New DAOs:

  • Default proposalUpdatablePeriod = 1 day
  • Use deployDeterministic() for predictable addresses

Related Changes

  • Consolidated GitHub workflow scripts for cleaner CI
  • Human-readable DEPLOY_SALT for deterministic deployments
  • Updated deployment workflows documentation

Version: 3.0.0
Tested on: Sepolia, Base Sepolia, Optimism Sepolia
Audit Status: See docs/governor-audit-readiness.md

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for creating and updating proposals via offline signatures, enabling multi-signer governance workflows.
    • Introduced an updatable proposal period allowing authorized changes to active proposals.
    • Enhanced vote-by-signature with nonce tracking and a unified signature format.
    • Added new proposal states and replacement tracking for modified proposals.
  • Documentation

    • Added comprehensive migration guide for Governor V2 frontend integration.
    • Added governance architecture and proposal lifecycle reference documentation.
    • Added protocol upgrade runbook for chains and DAOs.
    • Added audit readiness checklist for governance upgrades.

Review Change Stack

@coderabbitai

coderabbitai Bot commented Apr 21, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 166 files, which is 66 over the limit of 100.

To get a review, narrow the scope:
• coderabbit review --committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

Upgrade to a paid plan to raise the limit.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: aa01f926-f912-41e8-a52c-a92c56921d59

📥 Commits

Reviewing files that changed from the base of the PR and between 03e428f and e6679a2.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (171)
  • .env.example
  • .github/workflows/storage.yml
  • .github/workflows/test.yml
  • .gitignore
  • .gitmodules
  • .prettierignore
  • .prettierrc
  • .solhint.json
  • .storage-layout
  • README.md
  • addresses/1.json
  • addresses/10.json
  • addresses/11155111.json
  • addresses/11155420.json
  • addresses/7777777.json
  • addresses/8453.json
  • addresses/84532.json
  • addresses/999999999.json
  • deploys/10.merkle_property.txt
  • deploys/11155111.merkle_property.txt
  • deploys/11155111.version3_new.txt
  • deploys/11155111.version3_upgrade.txt
  • deploys/11155420.merkle_property.txt
  • deploys/11155420.version3_new.txt
  • deploys/11155420.version3_upgrade.txt
  • deploys/7777777.merkle_property.txt
  • deploys/8453.merkle_property.txt
  • deploys/84532.merkle_property.txt
  • deploys/84532.version3_new.txt
  • deploys/84532.version3_upgrade.txt
  • deploys/999999999.merkle_property.txt
  • docs/README.md
  • docs/deployment-workflows.md
  • docs/eas-proposal-candidates-schema.md
  • docs/governor-architecture.md
  • docs/governor-proposal-lifecycle.md
  • docs/internal-security-audit.md
  • docs/upgrade-runbook.md
  • docs/v3-audit-readiness.md
  • foundry.toml
  • lib/create3-factory
  • package.json
  • remappings.txt
  • script/.solhint.json
  • script/Constants.sol
  • script/DeployConstants.sol
  • script/DeployERC721RedeemMinter.s.sol
  • script/DeployHelpers.sol
  • script/DeployMerkleProperty.s.sol
  • script/DeployMerkleReserveMinter.s.sol
  • script/DeployNewDAO.s.sol
  • script/DeployV2Core.s.sol
  • script/DeployV2New.s.sol
  • script/DeployV2Upgrade.s.sol
  • script/DeployV3New.s.sol
  • script/DeployV3Upgrade.s.sol
  • script/GenerateRendererStorage.s.sol
  • script/GetInterfaceIds.s.sol
  • script/checkBuilderRewardsConfig.mjs
  • script/checkUpgradeStatus.mjs
  • script/generateMerkleTree.mjs
  • script/updateManagerOwner.mjs
  • src/VersionedContract.sol
  • src/auction/Auction.sol
  • src/auction/IAuction.sol
  • src/auction/storage/AuctionStorageV1.sol
  • src/auction/storage/AuctionStorageV2.sol
  • src/auction/types/AuctionTypesV1.sol
  • src/auction/types/AuctionTypesV2.sol
  • src/deployers/L2MigrationDeployer.sol
  • src/deployers/interfaces/ICrossDomainMessenger.sol
  • src/escrow/Escrow.sol
  • src/factory/DAOFactory.sol
  • src/factory/IDAOFactory.sol
  • src/governance/governor/Governor.sol
  • src/governance/governor/IGovernor.sol
  • src/governance/governor/ProposalHasher.sol
  • src/governance/governor/storage/GovernorStorageV1.sol
  • src/governance/governor/storage/GovernorStorageV2.sol
  • src/governance/governor/storage/GovernorStorageV3.sol
  • src/governance/governor/types/GovernorTypesV1.sol
  • src/governance/treasury/ITreasury.sol
  • src/governance/treasury/Treasury.sol
  • src/governance/treasury/storage/TreasuryStorageV1.sol
  • src/governance/treasury/types/TreasuryTypesV1.sol
  • src/lib/interfaces/IEIP712.sol
  • src/lib/interfaces/IERC1967Upgrade.sol
  • src/lib/interfaces/IERC721.sol
  • src/lib/interfaces/IERC721Votes.sol
  • src/lib/interfaces/IInitializable.sol
  • src/lib/interfaces/IOwnable.sol
  • src/lib/interfaces/IPausable.sol
  • src/lib/interfaces/IProtocolRewards.sol
  • src/lib/interfaces/IUUPS.sol
  • src/lib/interfaces/IVersionedContract.sol
  • src/lib/proxy/ERC1967Proxy.sol
  • src/lib/proxy/ERC1967Upgrade.sol
  • src/lib/proxy/UUPS.sol
  • src/lib/token/ERC721.sol
  • src/lib/token/ERC721Votes.sol
  • src/lib/utils/Address.sol
  • src/lib/utils/EIP712.sol
  • src/lib/utils/Initializable.sol
  • src/lib/utils/Ownable.sol
  • src/lib/utils/Pausable.sol
  • src/lib/utils/ReentrancyGuard.sol
  • src/lib/utils/SafeCast.sol
  • src/lib/utils/TokenReceiver.sol
  • src/manager/IManager.sol
  • src/manager/Manager.sol
  • src/manager/storage/ManagerStorageV1.sol
  • src/manager/types/ManagerTypesV1.sol
  • src/minters/ERC721RedeemMinter.sol
  • src/minters/MerkleReserveMinter.sol
  • src/token/IToken.sol
  • src/token/Token.sol
  • src/token/metadata/MetadataRenderer.sol
  • src/token/metadata/interfaces/IBaseMetadata.sol
  • src/token/metadata/interfaces/IPropertyIPFSMetadataRenderer.sol
  • src/token/metadata/renderers/BaseMetadata.sol
  • src/token/metadata/renderers/IBaseMetadata.sol
  • src/token/metadata/renderers/MerklePropertyIPFS/IMerklePropertyIPFS.sol
  • src/token/metadata/renderers/MerklePropertyIPFS/MerklePropertyIPFS.sol
  • src/token/metadata/renderers/PropertyIPFS/IPropertyIPFS.sol
  • src/token/metadata/renderers/PropertyIPFS/PropertyIPFS.sol
  • src/token/metadata/storage/MetadataRendererStorageV1.sol
  • src/token/metadata/storage/MetadataRendererStorageV2.sol
  • src/token/metadata/types/MetadataRendererTypesV1.sol
  • src/token/metadata/types/MetadataRendererTypesV2.sol
  • src/token/storage/TokenStorageV1.sol
  • src/token/storage/TokenStorageV2.sol
  • src/token/storage/TokenStorageV3.sol
  • src/token/types/TokenTypesV1.sol
  • src/token/types/TokenTypesV2.sol
  • test/.solhint.json
  • test/Auction.t.sol
  • test/DeployHelpers.t.sol
  • test/DeployV3New.t.sol
  • test/ERC721RedeemMinter.t.sol
  • test/Gov.t.sol
  • test/GovFuzz.t.sol
  • test/GovGasBenchmark.t.sol
  • test/GovUpgrade.t.sol
  • test/L2MigrationDeployer.t.sol
  • test/Manager.t.sol
  • test/MerklePropertyIPFS.t.sol
  • test/MerklePropertyIPFS_ENCODING.md
  • test/MerkleReserveMinter.t.sol
  • test/MetadataRenderer.t.sol
  • test/Token.t.sol
  • test/VersionedContractTest.t.sol
  • test/forking/Create3Factory.t.sol
  • test/forking/CrossChainDeterminism.t.sol
  • test/forking/TestBid.t.sol
  • test/forking/TestMainnetManagerUpgrade.t.sol
  • test/forking/TestPurpleDAOSystemUpgrade.t.sol
  • test/forking/TestUpdateMinters.t.sol
  • test/forking/TestUpdateOwners.t.sol
  • test/utils/Base64URIDecoder.sol
  • test/utils/MerkleTreeHelper.sol
  • test/utils/NounsBuilderTest.sol
  • test/utils/ViaIRTestHelper.sol
  • test/utils/mocks/LegacyGovernorV2.sol
  • test/utils/mocks/MockCrossDomainMessenger.sol
  • test/utils/mocks/MockERC1155.sol
  • test/utils/mocks/MockERC1271Wallet.sol
  • test/utils/mocks/MockERC721.sol
  • test/utils/mocks/MockImpl.sol
  • test/utils/mocks/MockPartialTokenImpl.sol
  • test/utils/mocks/MockProtocolRewards.sol
  • test/utils/mocks/WETH.sol

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Review on demand using usage pricing
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The PR title clearly and specifically summarizes the main changes: adding updatable proposals and signature-based sponsorship features to Governor v3.0.0.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/updatable-proposals

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@dan13ram
dan13ram force-pushed the feat/updatable-proposals branch from 5abbe4c to a3bc597 Compare May 20, 2026 13:55
@dan13ram
dan13ram force-pushed the feat/updatable-proposals branch from a2b9aaa to 03e428f Compare May 21, 2026 17:18

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 5

🧹 Nitpick comments (5)
test/GovGasBenchmark.t.sol (1)

338-374: ⚡ Quick win

Extract duplicated helper logic to shared test base (Lines 338-374).

_buildOrderedUpdateSignatures and _mintTokensToUsers are duplicated across benchmark/fuzz suites. Moving them into Gov.t.sol (or a shared helper mixin) reduces drift risk when signature payloads change again.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/GovGasBenchmark.t.sol` around lines 338 - 374, Move the duplicated
helper functions _buildOrderedUpdateSignatures and _mintTokensToUsers out of
GovGasBenchmark.t.sol into the shared test base (e.g., Gov.t.sol or a new helper
mixin) so all benchmark/fuzz suites use the same implementations; ensure the
moved _buildOrderedUpdateSignatures continues to reference
UPDATE_PROPOSAL_TYPEHASH, governor.DOMAIN_SEPARATOR(), and _encodeSignature and
that callers still pass proposer/nonce/deadline, and ensure _mintTokensToUsers
continues to use token.mint(), auction, token.transferFrom(...) and otherUsers;
after moving, remove the duplicates and update any imports/inheritance so tests
compile and reference the single shared helper.
test/GovFuzz.t.sol (1)

80-89: ⚡ Quick win

Improve fuzz effectiveness by removing high-rejection assumption (Lines 80-89).

vm.assume(expiredOffset <= block.timestamp) drops a large portion of fuzz cases after bounding to 365 days. Use a dynamic upper bound instead to keep the corpus dense.

Proposed change
-        // Bound to reasonable past range
-        expiredOffset = bound(expiredOffset, 1, 365 days);
+        // Bound to valid past range relative to current timestamp
+        uint256 maxExpiredOffset = block.timestamp < 365 days ? block.timestamp : 365 days;
+        expiredOffset = bound(expiredOffset, 1, maxExpiredOffset);
...
-        vm.assume(expiredOffset <= block.timestamp);
         uint256 deadline = block.timestamp - expiredOffset;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/GovFuzz.t.sol` around lines 80 - 89, The test currently uses
vm.assume(expiredOffset <= block.timestamp) which rejects many fuzz cases;
instead move the bounding of expiredOffset to after the vm.warp and replace
bound(expiredOffset, 1, 365 days) with a dynamic upper bound using the current
timestamp (e.g., bound(expiredOffset, 1, block.timestamp)), and remove the
vm.assume line; adjust the order so you call deployMock(), mintVoter1(),
createProposal(), vm.warp(... governor.proposalUpdatablePeriod() +
governor.votingDelay()), then compute expiredOffset via bound(expiredOffset, 1,
block.timestamp) and derive deadline = block.timestamp - expiredOffset.
test/GovUpgrade.t.sol (2)

271-273: ⚡ Quick win

Consider specifying the expected revert reason.

Using vm.expectRevert() without a specific error could mask bugs if the upgrade fails for an unexpected reason. Specifying the exact error would improve test reliability and help identify regressions faster.

♻️ Proposed enhancement

Looking at the Manager contract pattern in this codebase, the specific error should likely be related to the Manager's upgrade registration check. Consider adding the expected error message:

        // Attempt upgrade without registration should fail
        vm.prank(address(treasury));
-       vm.expectRevert();
+       vm.expectRevert(); // TODO: Add specific error from Manager contract
        governor.upgradeTo(address(newGovernorImpl));

Or if there's a specific error from the upgrade validation logic, use it:

-       vm.expectRevert();
+       vm.expectRevert(abi.encodeWithSignature("INVALID_UPGRADE(address,address)", address(governorImpl), address(newGovernorImpl)));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/GovUpgrade.t.sol` around lines 271 - 273, Replace the generic
vm.expectRevert() with a targeted expectRevert that asserts the exact revert
reason thrown by the Manager/upgrade validation so the test fails only for the
intended condition; locate the Manager contract's upgrade registration or
validation revert string (the error emitted by the function that blocks
unauthorized upgrades) and use vm.expectRevert(abi.encodePacked("<exact revert
string>")) (or the appropriate bytes/error selector) immediately before
vm.prank(address(treasury)); governor.upgradeTo(address(newGovernorImpl)) so the
test checks for that specific revert from governor.upgradeTo.

15-17: 💤 Low value

Consider removing the redundant setUp function.

The override only calls super.setUp() without any additional logic. When there's no child-specific setup, the function can be removed entirely and the parent's setUp will be called automatically.

♻️ Proposed cleanup
-    function setUp() public override {
-        super.setUp();
-    }
-
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/GovUpgrade.t.sol` around lines 15 - 17, The setUp() override in
GovUpgrade.t.sol is redundant because it only calls super.setUp(); remove the
entire function declaration (the setUp() override that calls super.setUp()) so
the parent setUp() will be used automatically; if you later need child-specific
initialization, reintroduce an override that calls super.setUp() and adds the
extra logic.
src/governance/governor/IGovernor.sol (1)

314-320: ⚡ Quick win

Expose the replacement successor in IGovernor.

state() can now return Replaced, but the interface does not expose the replacement id. The concrete contract already has a public proposalIdReplacedBy getter via storage, so callers compiled against IGovernor cannot follow the replacement chain from the interface alone.

💡 Suggested addition
+    /// `@notice` The replacement proposal id for a replaced proposal
+    /// `@param` proposalId The original proposal id
+    function proposalIdReplacedBy(bytes32 proposalId) external view returns (bytes32);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/governance/governor/IGovernor.sol` around lines 314 - 320, The IGovernor
interface must expose the replacement successor so callers can follow a Replaced
state chain; add a public getter declaration matching the concrete contract's
storage accessor (e.g., proposalIdReplacedBy) to IGovernor (signature like
proposalIdReplacedBy(bytes32) external view returns (bytes32)) so callers
compiled against IGovernor can retrieve the replacement proposal id when state()
returns Replaced; update the interface near the other proposal getters
(alongside getProposalSigners and proposalUpdatePeriodEnd).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/frontend-migration-guide.md`:
- Around line 331-338: The while-loop in getLatestProposalId incorrectly uses
the ethers v5 constant ethers.constants.HashZero; update the zero-hash check to
use the ethers v6 API by replacing ethers.constants.HashZero with
ethers.ZeroHash wherever getLatestProposalId calls governor.proposalIdReplacedBy
(and any similar replacement-chain checks) so the loop compares against the
correct zero hash constant.

In `@src/governance/governor/Governor.sol`:
- Around line 952-964: The update path currently allows arbitrarily many
signers; when handling an update in updateProposalBySigs (after receiving
newSigners from _validateUpdateSignaturesAndGetVotes but before calling
_replaceProposalWithSigners) enforce the same cap used by proposeBySigs by
checking newSigners.length <= MAX_PROPOSAL_SIGNERS and revert (e.g., with a new
or existing error) if exceeded; alternatively add the check inside
_validateUpdateSignaturesAndGetVotes so it never returns an oversized signer
array—this prevents unbounded gas/storage growth that would affect cancel() and
getProposalSigners().
- Around line 882-886: The loop in the helper copies old signer addresses into
proposalSigners[newProposalId] (using newSigners and _oldSigners), which
incorrectly carries prior approvals onto an unsigned replacement; remove the
copy so proposalSigners[newProposalId] remains empty (no push of _oldSigners
into newSigners) and ensure updateProposal()/updateProposalBySigs() flow
requires fresh signatures for the replacement (or explicitly document/implement
requiring updateProposalBySigs for persistent backing) so
getProposalSigners(newProposalId) and cancel() reflect only actual signers of
the new proposal.

In `@test/Gov.t.sol`:
- Around line 2728-2740: The test mints tokens to address(auction) so delegating
via token.delegate(address(wallet)) gives the ERC-1271 wallet zero votes; change
the minting so the wallet actually receives tokens before delegating (e.g. call
token.mint() while vm.prank(address(wallet)) or use the contract's
mintTo(wallet) helper if available) and make the same adjustment in the second
occurrence (the block around lines 2792-2800) so the wallet holds tokens prior
to token.delegate(address(wallet)) and the mixed-signer assertions exercise
contract voting weight.
- Around line 1489-1490: The tests never exercise non-zero proposalThreshold
because total supply after mintVoter1() is too small so
governor.updateProposalThresholdBps(1) still rounds proposalThreshold() to 0;
fix by increasing circulating supply before calling
governor.updateProposalThresholdBps (either mint additional tokens in
mintVoter1() or call an extra mint helper so totalSupply >= ceil(requiredBps *
denominator / 10000)), or use a much larger BPS value so that
proposalThreshold() > 0 for the current supply; update the test cases around
mintVoter1(), governor.updateProposalThresholdBps(...), and assertions on
proposalThreshold() to ensure the non-zero branch is actually reached.

---

Nitpick comments:
In `@src/governance/governor/IGovernor.sol`:
- Around line 314-320: The IGovernor interface must expose the replacement
successor so callers can follow a Replaced state chain; add a public getter
declaration matching the concrete contract's storage accessor (e.g.,
proposalIdReplacedBy) to IGovernor (signature like proposalIdReplacedBy(bytes32)
external view returns (bytes32)) so callers compiled against IGovernor can
retrieve the replacement proposal id when state() returns Replaced; update the
interface near the other proposal getters (alongside getProposalSigners and
proposalUpdatePeriodEnd).

In `@test/GovFuzz.t.sol`:
- Around line 80-89: The test currently uses vm.assume(expiredOffset <=
block.timestamp) which rejects many fuzz cases; instead move the bounding of
expiredOffset to after the vm.warp and replace bound(expiredOffset, 1, 365 days)
with a dynamic upper bound using the current timestamp (e.g.,
bound(expiredOffset, 1, block.timestamp)), and remove the vm.assume line; adjust
the order so you call deployMock(), mintVoter1(), createProposal(), vm.warp(...
governor.proposalUpdatablePeriod() + governor.votingDelay()), then compute
expiredOffset via bound(expiredOffset, 1, block.timestamp) and derive deadline =
block.timestamp - expiredOffset.

In `@test/GovGasBenchmark.t.sol`:
- Around line 338-374: Move the duplicated helper functions
_buildOrderedUpdateSignatures and _mintTokensToUsers out of
GovGasBenchmark.t.sol into the shared test base (e.g., Gov.t.sol or a new helper
mixin) so all benchmark/fuzz suites use the same implementations; ensure the
moved _buildOrderedUpdateSignatures continues to reference
UPDATE_PROPOSAL_TYPEHASH, governor.DOMAIN_SEPARATOR(), and _encodeSignature and
that callers still pass proposer/nonce/deadline, and ensure _mintTokensToUsers
continues to use token.mint(), auction, token.transferFrom(...) and otherUsers;
after moving, remove the duplicates and update any imports/inheritance so tests
compile and reference the single shared helper.

In `@test/GovUpgrade.t.sol`:
- Around line 271-273: Replace the generic vm.expectRevert() with a targeted
expectRevert that asserts the exact revert reason thrown by the Manager/upgrade
validation so the test fails only for the intended condition; locate the Manager
contract's upgrade registration or validation revert string (the error emitted
by the function that blocks unauthorized upgrades) and use
vm.expectRevert(abi.encodePacked("<exact revert string>")) (or the appropriate
bytes/error selector) immediately before vm.prank(address(treasury));
governor.upgradeTo(address(newGovernorImpl)) so the test checks for that
specific revert from governor.upgradeTo.
- Around line 15-17: The setUp() override in GovUpgrade.t.sol is redundant
because it only calls super.setUp(); remove the entire function declaration (the
setUp() override that calls super.setUp()) so the parent setUp() will be used
automatically; if you later need child-specific initialization, reintroduce an
override that calls super.setUp() and adds the extra logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fdd6265e-2632-4e51-a195-961ac22e114d

📥 Commits

Reviewing files that changed from the base of the PR and between cfb42d8 and 03e428f.

📒 Files selected for processing (20)
  • docs/README.md
  • docs/deployment-workflows.md
  • docs/frontend-migration-guide.md
  • docs/governor-architecture.md
  • docs/governor-audit-readiness.md
  • docs/governor-proposal-lifecycle.md
  • docs/mainnet-v2-upgrade-runbook.md
  • docs/upgrade-runbook.md
  • package.json
  • src/governance/governor/Governor.sol
  • src/governance/governor/IGovernor.sol
  • src/governance/governor/storage/GovernorStorageV3.sol
  • src/governance/governor/types/GovernorTypesV1.sol
  • test/Gov.t.sol
  • test/GovFuzz.t.sol
  • test/GovGasBenchmark.t.sol
  • test/GovUpgrade.t.sol
  • test/MetadataRenderer.t.sol
  • test/Token.t.sol
  • test/utils/mocks/MockERC1271Wallet.sol
💤 Files with no reviewable changes (1)
  • docs/mainnet-v2-upgrade-runbook.md

Comment thread docs/frontend-migration-guide.md Outdated
Comment on lines +331 to +338
async function getLatestProposalId(proposalId) {
let currentId = proposalId;
let replacedBy = await governor.proposalIdReplacedBy(currentId);

// Follow replacement chain to get latest version
while (replacedBy !== ethers.constants.HashZero) {
currentId = replacedBy;
replacedBy = await governor.proposalIdReplacedBy(currentId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In ethers.js v6, what is the equivalent of ethers.constants.HashZero from ethers.js v5?

💡 Result:

In ethers.js v6, the equivalent of ethers.constants.HashZero from v5 is ethers.ZeroHash [1][2]. In v5, constants were grouped under the ethers.constants object [3]. In v6, these constants have been moved to the top-level ethers object [1][4]. Similarly, ethers.constants.AddressZero has been replaced by ethers.ZeroAddress [1][2].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE="docs/frontend-migration-guide.md"
nl -ba "$FILE" | sed -n '320,350p'

Repository: BuilderOSS/nouns-protocol

Length of output: 112


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE="docs/frontend-migration-guide.md"
cat -n "$FILE" | sed -n '320,350p'

Repository: BuilderOSS/nouns-protocol

Length of output: 1010


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE="docs/frontend-migration-guide.md"

echo "== HashZero occurrences =="
rg -n "ethers\.constants\.HashZero" "$FILE" || true

echo "== ZeroHash occurrences =="
rg -n "ethers\.ZeroHash" "$FILE" || true

echo "== Context around HashZero =="
rg -n "ethers\.constants\.HashZero" "$FILE" -C 5 || true

Repository: BuilderOSS/nouns-protocol

Length of output: 624


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE="docs/frontend-migration-guide.md"

rg -n "ethers\.?js v6|ethers v6|\bv6\b|ZeroAddress|constants\.HashZero|HashZero|ethers\.ZeroHash" "$FILE" || true

# Also grab the section header(s) around line 331
sed -n '280,380p' "$FILE" | nl -ba -w2 -s': ' | head -n 120

Repository: BuilderOSS/nouns-protocol

Length of output: 215


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE="docs/frontend-migration-guide.md"

echo "== Around ethers.js v6 section (90-180) =="
sed -n '90,180p' "$FILE"

echo
echo "== Around snippet (300-370) =="
sed -n '300,370p' "$FILE"

Repository: BuilderOSS/nouns-protocol

Length of output: 3969


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE="docs/frontend-migration-guide.md"

echo "== ethers.js section headings =="
rg -n "^#### Using ethers\.js v" "$FILE" || true

echo
echo "== ethers.constants.* occurrences =="
rg -n "ethers\.constants\." "$FILE" || true

Repository: BuilderOSS/nouns-protocol

Length of output: 225


Update replacement-chain zero-hash check to ethers v6 (ethers.ZeroHash).

docs/frontend-migration-guide.md (line 336) uses ethers.constants.HashZero (ethers v5 API); in ethers v6 this should be ethers.ZeroHash to prevent copy/paste failures.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/frontend-migration-guide.md` around lines 331 - 338, The while-loop in
getLatestProposalId incorrectly uses the ethers v5 constant
ethers.constants.HashZero; update the zero-hash check to use the ethers v6 API
by replacing ethers.constants.HashZero with ethers.ZeroHash wherever
getLatestProposalId calls governor.proposalIdReplacedBy (and any similar
replacement-chain checks) so the loop compares against the correct zero hash
constant.

Comment thread src/governance/governor/Governor.sol Outdated
Comment thread src/governance/governor/Governor.sol Outdated
Comment thread test/Gov.t.sol
Comment thread test/Gov.t.sol
@dan13ram
dan13ram force-pushed the feat/updatable-proposals branch from 64e8260 to 9127436 Compare May 26, 2026 15:37
@dan13ram
dan13ram force-pushed the feat/updatable-proposals branch from 8027c1f to 3983757 Compare May 26, 2026 16:39
dan13ram and others added 9 commits July 15, 2026 07:36
Major changes:
- Implement CREATE3 for bytecode-independent cross-chain determinism
- Fix Manager proxy front-running vulnerability with atomic initialization
- Remove WETH from Manager (kept only in Auction as immutable)
- Keep builderRewardsRecipient as Manager immutable (changeable via upgrade)

Security improvements:
- Manager proxy includes initialization in constructor to prevent front-running
- All implementations deployed via CREATE3 for identical addresses across chains
- Added CREATE3 factory verification tests for all supported networks

Documentation updates:
- Created comprehensive v3-audit-readiness.md
- Fixed broken links and references
- Updated all deployment workflows for atomic initialization
- Fixed EAS proposalId hash calculation bug
- Fixed MAX_PROPOSAL_SIGNERS documentation (16 not 32)

Breaking changes:
- Removed V2 deployment scripts
- Removed frontend migration guides
- Manager constructor signature changed (removed WETH parameter)
This commit fixes 5 critical issues identified in the V3 CREATE3 implementation:

**Finding 1 (High): test:unit requires live fork RPCs**
- Moved test/Create3Factory.t.sol to test/forking/Create3Factory.t.sol
- Fixed import path (../script → ../../script) after moving to forking subdirectory
- Fixed assembly usage: added local variable to use in extcodesize()
- Fixed function visibility: removed view modifier (assertGt modifies state)
- CREATE3Factory tests run fork tests against live networks, making unit suite depend on RPC env vars
- Moving to forking/ directory separates fork tests from unit tests

**Finding 2 (High): CREATE3 test accessibility**
- Changed DeployHelpers.CREATE3_FACTORY from internal to public constant
- Enables test files to access CREATE3_FACTORY address
- Fixes compilation error in Create3Factory.t.sol:11

**Finding 3 (High): fresh V3 deploy fails on mainnet**
- Removed L2MigrationDeployer from DeployV3New.s.sol entirely
- Removed import, struct field, constructor parameter, deployment code, file writing, and logging
- Removed CrossDomainMessenger dependency which doesn't exist in addresses/1.json
- L2MigrationDeployer will have separate deployment script for L2 chains

**Finding 4 (Medium): Zora support removal**
- Removed Zora and Zora Sepolia from foundry.toml RPC endpoints
- Removed deploy:zora script from package.json
- Removed Zora from docs/deployment-workflows.md supported networks list
- Removed Zora test functions from Create3Factory.t.sol (before moving to forking/)
- CREATE3 factory manifests don't include Zora deployment

**Finding 5 (Low): stale Manager init documentation**
- Updated docs/v3-audit-readiness.md lines 88-89
- Updated docs/deployment-workflows.md line 129
- Changed from "empty init data + separate initialization" to "atomic initialization"
- Documents current implementation: initialization data included in proxy constructor
- Clarifies security benefit: prevents front-running attacks
- Removed L2MigrationDeployer from deployment-workflows.md deterministic list
- Removed crossDomainMessenger from chain-specific parameters list

**Test Suite Separation Fix:**
- Updated package.json test:unit script to explicitly exclude forking tests
- Changed from `--match-path 'test/*.sol'` to `--match-path 'test/**/*.sol' --no-match-path 'test/forking/**/*.sol'`
- Ensures GitHub workflows don't run fork tests in unit test suite
- Only test:fork should run the forking tests that require live RPC connections

**Build/Test Status:**
- ✅ forge build: successful (27 files compiled)
- ✅ yarn test:unit: runs only unit tests (excludes Create3Factory forking tests)
- ✅ All compilation errors fixed
- ✅ No new test failures

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Implement DAOFactory as canonical deployer for DAO proxies
- Update Manager to use DAOFactory instead of CREATE3Factory directly
- Deploy CREATE3Factory via CREATE2 (Nick's factory) for deterministic addresses across chains
- Update all forking tests to deploy factories as needed at current blockchain state
- Fix CrossChainDeterminism test to use vm.startPrank for proper owner context
- Fix TestMainnetManagerUpgrade to deploy DAOFactory via CREATE3 in setUp
- Fix TestPurpleDAOSystemUpgrade to deploy DAOFactory via CREATE3 in setUp

Test results: 37/38 forking tests pass (97.4%)
- CrossChainDeterminism has 1 test with architectural issue: DAOFactory constructor ONLY_OWNER check fails during CREATE3 deployment because msg.sender is the CREATE3 proxy, not the Manager

This enables identical DAO addresses across chains despite different Manager addresses.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Pin fork blocks to specific heights (mainnet: 21200000, optimism: 125000000)
  This prevents test failures due to changing on-chain state and ensures reproducible tests
  Fixes ONLY_OWNER() error that occurred when forking at latest block

- Add founder array length check in Manager._deployDeterministic
  Prevents array out-of-bounds panic when deploying deterministic DAOs
  Now validates that founder array is not empty before accessing first element

- Update test_ActualDeploymentMatchesPrediction to provide founder params
  Tests now properly create minimal founder configuration for DAO deployment

All 42 forking tests now pass (5/5 CrossChainDeterminism, 10/10 TestMainnetManagerUpgrade,
18/18 TestPurpleDAOSystemUpgrade, 8/8 Create3Factory, 1/1 TestUpdateOwners)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Update deployment-workflows.md:
  - Add DAOFactory as canonical deployer for cross-chain DAO determinism
  - Explain DAOFactory deployment via CREATE3 with deterministic address
  - Update "How It Works" section to show DAOFactory as deployer
  - Add DAOFactory details to deployment factories section
  - Clarify that Manager references DAOFactory as immutable

- Update v3-audit-readiness.md:
  - Add DAOFactory to key feature additions
  - Add DAOFactory security considerations section
  - Document constructor validation (msg.sender == manager)
  - Document deployment authorization (only bound Manager can deploy)
  - Add DAOFactory to test coverage section
  - Add DAOFactory authorization to high-priority audit areas

DAOFactory enables cross-chain deterministic DAO deployments despite different
Manager addresses on each chain. Each Manager implementation is bound to its own
DAOFactory instance via immutable reference.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit includes three major improvements:

1. Architecture: Remove ImplementationParams struct
   - Removed ImplementationParams from IManager interface
   - Updated Manager.deployDeterministic() to use immutable implementations
   - Simplified Manager._deployDeterministicProxies() signature
   - Updated all test files to remove ImplementationParams usage
   - Implementation addresses now come exclusively from Manager immutables

2. Code Quality: Consolidate duplicate helper functions
   - Added addressToString(), bytes32ToString(), char() to DeployHelpers library
   - Updated all deploy scripts to use centralized implementations
   - Removed 100+ lines of duplicated code across 5 script files
   - Single source of truth for string conversion utilities

3. Build & Lint: Fix all warnings and standardize configuration
   - Fixed Prettier formatting issues (trailing whitespace, lib/ ignore)
   - Added global lint suppressions in foundry.toml for common patterns
   - Suppressed dependency warnings (node_modules/, lib/)
   - Removed all 26 unused imports across codebase
   - Fixed deprecated transfer() call in WETH mock
   - Fixed all NatSpec documentation warnings
   - Achieved clean build with zero compilation warnings

4. Documentation: Comprehensive accuracy review and updates
   - CRITICAL: Removed all non-existent ImplementationParams references
   - Fixed DAOFactory method name (deploy → deployProxy) and CREATE3 description
   - Added 130-line DAOFactory Architecture section explaining:
     * Why DAOFactory exists (solves cross-chain Manager address problem)
     * How DAOFactory works (deployment flow, security model)
     * Bootstrap deployment sequence (circular dependency resolution)
   - Fixed constructor validation descriptions
   - Updated all code examples to match actual contract signatures
   - Verified all documentation against V3 implementation

Files changed: 36 files (+466, -470 lines)
- Architecture: Manager.sol, IManager.sol, ManagerTypesV1.sol
- Scripts: DeployHelpers.sol, DeployV3*.sol, DeployERC721RedeemMinter.sol
- Tests: Manager.t.sol, CrossChainDeterminism.t.sol, TestMainnetManagerUpgrade.t.sol
- Config: foundry.toml, .prettierignore, .solhint.json
- Docs: deployment-workflows.md (+130 lines DAOFactory section), v3-audit-readiness.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
The test was failing because it was using old Token/Auction/etc implementations
from the forked mainnet, which had the old initialize() signature.

Changed _deployManagerImpl() to deploy NEW implementations with the updated
signatures instead of reusing the old ones from currentManager.

This ensures the test uses implementations compatible with the current codebase.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
…umentation

This commit addresses multiple findings from the security audit:

- Add PROPERTY_HAS_NO_ITEMS error to prevent division-by-zero during token minting
- Implement two-layer validation in both MetadataRenderer.sol and PropertyIPFS.sol:
  1. Early check when adding properties without items
  2. Post-processing validation to ensure all new properties have items
- Add comprehensive test coverage in MetadataRenderer.t.sol

- Add 9 comprehensive unit tests across 3 test files:
  - DeployHelpers.t.sol: CREATE3 prediction accuracy, salt reuse, stability
  - DeployV3New.t.sol: Full deployment validation, binding checks, salt derivation
  - Manager.t.sol: Prediction consistency before/after deployment
- All 51 tests pass, ensuring CREATE3 deployment correctness

- Enhance Manager constructor to validate DAOFactory interface and binding
- Ensure factory implements IDAOFactory.manager() and is bound to correct Manager
- Add INVALID_FACTORY_CONTRACT and INVALID_FACTORY_BINDING errors

- Add comprehensive NatSpec to proposeBySigs and updateProposalBySigs
- Document gas implications of signature validation before threshold checks
- Explain why early threshold check is not beneficial

- Document BREAKING CHANGE in castVoteBySig function signature and EIP-712 typehash
- V2→V3 changes: Added nonce parameter, changed VOTE_TYPEHASH structure
- Note that old V2 signatures are cryptographically invalid and cannot be reused
- Update Governor.sol, IGovernor.sol, and governor-architecture.md

- Add CREATE3Factory library as git submodule
- Update deployment scripts to use CREATE3 deterministic deployment
- Consolidate deployment helpers and validation logic

Test Results: 51/51 tests passing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit addresses code quality improvements and documentation enhancements
following security audit preparation.

## Core Improvements

**1. MetadataRenderer Entropy Fix (Security)**
- Change `blockhash(block.number)` → `blockhash(block.number - 1)`
  - Previous: always returned 0 (current block hash unavailable)
  - Now: uses actual previous block hash for real entropy
- Change `block.coinbase` → `block.prevrandao`
  - More manipulation-resistant post-Merge RANDAO beacon
  - Better than miner-controlled coinbase address
- Applies to: MetadataRenderer.sol, PropertyIPFS.sol
- Impact: Improved pseudo-randomness for NFT trait generation
- All tests pass (665 tests, 0 failures)

**2. Solhint Warnings Resolution**
- Add missing @notice tags (Manager.sol, MerklePropertyIPFS.sol)
- Add missing @return tag (_getFactoryManager)
- Suppress function-max-lines for _addProperties (91-93 lines, complex logic)
- Suppress unsafe-cheatcode warnings globally in foundry.toml
  - Legitimate use in test/ and script/ directories
  - Does not affect production contracts

**3. Enhanced NatSpec Documentation**
- IGovernor.sol: Add comprehensive docs to proposeBySigs/updateProposalBySigs
  - CRITICAL ordering requirements
  - Gas warnings (~30k per signature)
  - JavaScript sorting example
  - All validation rules documented

## Documentation Updates

**1. governor-proposal-lifecycle.md**
- Add 137-line "Signature Ordering Requirements" section
- Gas cost breakdown table (1-16 signers)
- Sorting examples: JavaScript, Solidity, Python
- Pre-flight validation pattern
- Common pitfalls and correct usage

**2. v3-audit-readiness.md**
- Add "NFT Metadata Randomness" security invariant section
- Document entropy sources and manipulation resistance
- Update deployment checklist (prepare:v3-upgrade)

**3. Manager.sol Comments**
- Add detailed rationale for _validateDAOFactory placement
- Explain why validation happens in deployDeterministic, not constructor
- Document gas cost tradeoff (~2.6k gas for better UX)

## Testing
- ✅ All 665 tests passing
- ✅ forge build succeeds
- ✅ yarn lint passes (no warnings)
- ✅ No functional changes to core logic

## Files Changed (26 total)
- Core: MetadataRenderer.sol, PropertyIPFS.sol, IGovernor.sol, Manager.sol
- Config: foundry.toml, package.json
- Docs: 5 documentation files
- Tests: 6 test files
- Scripts: 6 deployment scripts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
dan13ram and others added 3 commits July 20, 2026 21:01
Consolidates findings from 8 audit iterations (4 Claude, 4 GPT versions) into
a single authoritative internal security audit document with full traceability.

Key features:
- All 18 unique findings organized by severity (Critical/High/Medium/Low/Info)
- 100% implementation rate (15/15 actionable findings resolved)
- Rich GitHub links to all commits with line numbers
- Detailed code evidence and verification commands
- Complete appendices with commit timeline and testing procedures

All findings have been validated, deduplicated, and tracked through resolution
with commit SHAs, file paths with line numbers, and verification steps.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
… parameter

- Fix DeployHelpers.deployViaCreate3() to accept deployerAddress parameter
- Update all 24 call sites in deployment scripts and tests
- Add regression tests for broadcast context behavior
- Simplify predictCreate3Address() to use factory's getDeployed()
- Fix forking tests referencing removed DeployHelpers.predictAddress()

Resolves security issue where internal function used msg.sender (script address)
instead of broadcaster address for CREATE3 namespace verification.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add comprehensive NatSpec for MerklePropertyIPFS.setAttributes() explaining
  intentional pre-mint attribute setting capability for reveal workflows
- Document attribute preservation logic in PropertyIPFS.onMinted()
- Add tests validating pre-mint workflow and attribute preservation
- Fix markdown table formatting in internal-security-audit.md

Pre-mint attribute setting enables:
- Merkle allowlists with predetermined traits
- Reveal mechanics where attributes are committed before minting
- Gas-optimized batch operations

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
dan13ram and others added 2 commits July 20, 2026 22:11
- Add F-19 (High): CREATE3 deployment namespace verification bug
  - Documents security issue where deployViaCreate3() used wrong deployer
  - Includes resolution via explicit deployerAddress parameter
  - References commit 846bdfc with full implementation details

- Add F-20 (Low): Pre-mint attribute setting as intentional design
  - Originally reported as Medium severity bug
  - Downgraded to Low after analysis showed intentional behavior
  - Documents why pre-mint is required for reveal mechanics
  - References commit 17650bd with comprehensive NatSpec

- Update metrics: 18 → 20 findings, High 2 → 3, Low 3 → 4
- Add Phase 4 to commit timeline (Post-Audit Security Hardening)
- Add verification commands for new regression tests

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Corrected arithmetic error in summary metrics:
- Total unique findings: 20 → 19
- Actionable findings resolved: 17/17 → 16/16
- Medium severity findings: 8 → 7

Root cause: Medium findings section has 7 entries (F-05 through F-12),
not 8 as incorrectly stated in the summary.

Updated 7 locations throughout the document to reflect accurate counts:
- Line 6: Status line
- Line 21: Total unique findings
- Line 24: Medium severity count
- Line 28: Implementation rate
- Line 51: Table of contents
- Line 1680: Conclusion summary
- Line 1733: Final status line

Arithmetic verification: 2 Critical + 3 High + 7 Medium + 4 Low + 3 Info = 19 total (16 actionable)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
dan13ram and others added 4 commits July 21, 2026 13:45
Add proposalUpdatablePeriod as a configurable parameter in GovParams,
allowing new DAOs to set their proposal updatable period during deployment
instead of using a hardcoded default.

Changes:
- Add proposalUpdatablePeriod to GovParams struct (IManager.sol)
- Add proposalUpdatablePeriod parameter to Governor.initialize()
- Remove DEFAULT_PROPOSAL_UPDATABLE_PERIOD constant (no longer needed)
- Add MIN_PROPOSAL_UPDATABLE_PERIOD = 0 constant for consistency
- Update Manager._initializeDAO() to pass proposalUpdatablePeriod
- Fix variable shadowing issue in Governor.initialize()
- Update deployment scripts to set proposalUpdatablePeriod: 1 days
- Update all test helpers and test cases
- Update LegacyGovernorV2 mock for V3 Manager compatibility

Impact:
- New V3 DAOs: Can configure proposalUpdatablePeriod at deployment
- Existing DAOs: Unaffected (already initialized)
- Upgraded DAOs: Start with proposalUpdatablePeriod = 0 (disabled),
  can enable via updateProposalUpdatablePeriod() governance proposal

All 669 tests pass including 7 upgrade path tests (V2 → V3).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Update governor-architecture.md to reflect deployment-time configurability
- Add GovParams documentation to deployment-workflows.md including all 7 parameters
- Update upgrade-runbook.md for new deployment behavior
- Add post-audit change note to internal-security-audit.md with commit hash and risk assessment

All documentation now accurately reflects that proposalUpdatablePeriod is:
- Configurable during deployment via GovParams (default: 1 day)
- Valid range: 0 (disabled) to 24 weeks
- Set to 0 for upgraded DAOs, enabling via governance proposal

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Main changes:
- Add comprehensive README.md with project overview, architecture, features, and usage
- Fix CREATE2 → CREATE3 references for Manager deployments
- Correct governance parameter defaults to match deployment script (2 days for votingDelay/votingPeriod)
- Update nouns.wtf → nouns.build in resources
- Add internal-security-audit.md to docs/README.md index
- Document deployers (L2 migration) and factory (CREATE3 determinism) accurately

Key sections in new README:
- Overview and core contract architecture
- V3 features (updatable proposals, signed proposals, deterministic deployments)
- Installation and development workflows
- Deployment guides with accurate GovParams defaults
- Testing information (630 unit + 39 fork tests)
- Security and audit references
- Documentation index

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Update contract addresses for three testnets after V3 redeployment:
- Sepolia (11155111)
- OP Sepolia (11155420)
- Base Sepolia (84532)

New V3 deployment addresses (deterministic across all chains):
- Manager: 0xe5a7373d53ac4454996a9673b4a7a455b40f1150
- ManagerImpl: 0xe03c76388be1c5008e8aee10534dc68c290c8783
- DAOFactory: 0x9812abdc27f2a6100ff64ac458e77658d9dbbd73
- TokenImpl: 0xa97ab235bc6234b871ffbe688cd478b743f6ac07
- MetadataRendererImpl: 0x63d1f81efb4c47ebfa77dc9841932c360c39d6ce
- MerklePropertyIPFSImpl: 0xa9fffad38bbf370f492f5abbe23313a9d8e4c63f
- AuctionImpl: 0xe0f2982e725f90fa0394eeb8becc3c1aaa0cd6a1
- TreasuryImpl: 0x5ef26412f6b3ea35099f53ba9a032ec15a4b77a4
- GovernorImpl: 0x04515024ad1f9bd097db4983914a23a6dcb65751
- MerkleReserveMinter: 0x109645180fda28ba450a26623e3c8bf4e804f134
- ERC721RedeemMinter: 0xf44b9239062eb1fc13ac7f27a278f9ca81787428

All addresses are identical across the three testnets, confirming successful
CREATE3 deterministic deployment via DAOFactory.

Chain-specific infrastructure addresses (WETH, BuilderRewardsRecipient, etc.)
preserved from previous configuration.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@dan13ram
dan13ram marked this pull request as ready for review July 21, 2026 11:55
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.

1 participant