Skip to content

fix(contracts): audit remediation, Holacracy compliance, ERC-8004 agents#48

Merged
Skanislav merged 1 commit into
devfrom
fix/audit-remediations-and-optimizations
Apr 16, 2026
Merged

fix(contracts): audit remediation, Holacracy compliance, ERC-8004 agents#48
Skanislav merged 1 commit into
devfrom
fix/audit-remediations-and-optimizations

Conversation

@Skanislav

Copy link
Copy Markdown
Contributor

Summary

Addresses all Critical/High/Medium findings from the security audit, adds gas optimizations, Holacracy-compliant facilitator authority, ERC-8004 agent identity, and proposal expiry.

Security Fixes (Audit Findings)

  • C-1: RoleRegistry.setGovernanceProcess restricted to OrgFactory — prevents front-running governance hijack
  • H-1: ActionVoting stores orgId at init, uses it for admin checks (was passing circleId as orgId)
  • H-2: MeetingComponentsFactory.deploy() gated by org admin check — was permissionless
  • H-3: MeetingFactory stores orgId, validates on all public functions — prevents cross-org escalation
  • M-1: adoptProposal enforces zero open objections before adoption
  • M-2: removeOrgAdmin prevents last-admin lockout (tracks _orgAdminCount)
  • M-3: _mintInitialTokens validates initialHolders.length == initialAmounts.length
  • L-1: GovToken.setMinter(address(0)) reverts with ZeroAddress()
  • L-5: GovToken emits MinterChanged event on minter transfer
  • L-7: Election change type unassigns previous lead before assigning new one (3-param encoding)

Holacracy Compliance (§5.3.3-5.3.4)

  • resolveObjection requires objector (withdrawal) or circle facilitator (dismissal) — Lead Link/admin cannot unilaterally overrule objections
  • setCircleFacilitator(circleId, facilitator) — admin-gated setter (bridge until elections run onchain)

New Features

  • ERC-8004 agent identity: linkAgentIdentity(orgId, agentRegistry, agentId) — org members can link onchain agent NFTs
  • Proposal expiry: MAX_PROPOSAL_AGE (14 days), discardExpiredProposal is permissionless
  • Events: GovernanceProcessSet, CircleFacilitatorSet, AgentIdentityLinked, MinterChanged

Gas Optimizations

  • _assertOrgAdmin reads storage slot directly instead of loading full Organization struct (-24k gas per admin call, 30-42% savings)
  • setRoleRegistryGovernanceProcess uses storage pointer
  • Remove redundant zero writes in createOrganization
  • keccak256('role') precomputed as constant

Breaking Changes

  • OrganizationFactory constructor now takes 3 params: (roleRegistryImpl, ensRegistrar, meetingComponentsFactory)
  • RoleRegistry.initialize(address _factory) — was initialize()
  • MeetingFactory.initialize(uint256 _orgId, ...) — was initialize(address, address)
  • ActionVoting.initialize(uint256 _orgId, ...) — was initialize(address, address, address)
  • Election encoding: (roleId, newLead, previousLead) — was (roleId, lead)
  • resolveObjection auth: facilitator, not admin
  • Deploy scripts updated

Test plan

  • All 130 Foundry tests pass (was 97 — 33 new tests added)
  • New GovToken test suite (15 tests)
  • Facilitator resolution flow tested (set facilitator, resolve multiple objections, adopt)
  • Admin-cannot-resolve explicitly tested
  • Proposal expiry: boundary test at exactly 14 days, permissionless discard
  • ERC-8004: ownership verification, membership gate, event emission
  • Last-admin lockout protection tested
  • Array length mismatch tested
  • Cross-org ID mismatch tested
  • Election with previous lead unassignment tested
  • Pre-commit hooks pass (lint, type-check, Solidity lint)
  • Frontend wagmi generate after merge to regenerate TS bindings
  • Redeploy infrastructure (constructor signature changed)

🤖 Generated with Claude Code

…ance

Addresses all Critical/High/Medium findings from the security audit plus
architectural improvements informed by ethskills analysis.

Security fixes:
- C-1: RoleRegistry.setGovernanceProcess restricted to factory (OrgFactory)
- H-1: ActionVoting stores orgId at init, uses it for admin checks
- H-2: MeetingComponentsFactory.deploy() gated by org admin check
- H-3: MeetingFactory stores orgId, validates on all public functions
- M-1: adoptProposal enforces zero open objections before adoption
- M-2: removeOrgAdmin prevents last-admin lockout
- M-3: _mintInitialTokens validates array length match
- L-1: GovToken.setMinter(address(0)) reverts with ZeroAddress
- L-5: GovToken emits MinterChanged event on minter transfer
- L-7: Election change type unassigns previous lead before assigning new

Holacracy compliance (§5.3.3-5.3.4):
- resolveObjection requires objector or circle facilitator (not admin)
- setCircleFacilitator(circleId, facilitator) admin-gated setter
- Lead Link / admin cannot unilaterally overrule objections

Gas optimizations:
- _assertOrgAdmin reads storage directly (-24k gas per admin call)
- setRoleRegistryGovernanceProcess uses storage pointer
- Remove redundant zero writes in createOrganization
- Remove no-op status=Draft assignment
- Precompute keccak256('role') as constant

New features:
- ERC-8004 agent identity: linkAgentIdentity(orgId, registry, agentId)
- Proposal expiry: MAX_PROPOSAL_AGE (14 days), permissionless discard
- GovernanceProcessSet event on RoleRegistry
- CircleFacilitatorSet event on MeetingFactory

Tests: 130 passing (was 97), including 33 new tests covering all changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Skanislav Skanislav merged commit d90090c into dev Apr 16, 2026
6 checks passed
@Skanislav Skanislav deleted the fix/audit-remediations-and-optimizations branch April 16, 2026 17:04
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