Skip to content

feat: add role-based access control and emergency pause#4

Merged
nikitavorobie merged 29 commits into
mainfrom
developing
Dec 12, 2025
Merged

feat: add role-based access control and emergency pause#4
nikitavorobie merged 29 commits into
mainfrom
developing

Conversation

@nikitavorobie

Copy link
Copy Markdown
Owner

Implement AccessControlUpgradeable with 4-tier role hierarchy.
Add emergency pause mechanism with comprehensive tests.
Deploy fresh verified contracts to Base Sepolia and Mainnet.

nikitavorobie and others added 29 commits December 2, 2025 11:20
Add support for creating and managing multiple isolated treasury vaults.
Each vault maintains independent signer sets, weights, and approval
thresholds for flexible DAO governance.

- Add VaultInfo struct for vault metadata and configuration
- Implement per-vault storage mappings for signers
- Add createVault, addSigner, removeSigner, setThreshold functions
- Reserve 46 storage slots for future upgrades
- Maintain backward compatibility with existing storage layout
Comprehensive test suite for multi-vault operations including vault
creation, signer management, threshold configuration, and access control.
Tests verify proper isolation between vault instances and edge cases.
Implement automated upgrade workflows for Base Sepolia and Mainnet with
safety confirmations. Add deployment metadata structure for tracking
contract versions and upgrade history across networks.
Remove unnecessary documentation and dev files. Rewrite README with
focus on use cases and practical examples instead of development info.

Removed:
- CODE_OF_CONDUCT.md
- CONTRIBUTING.md
- DEPLOYMENTS.md
- SECURITY.md

Updated:
- README.md - simplified with use cases and deployed addresses
Clean multi-vault only implementation without backward compatibility layer.

Breaking changes:
- Remove global signer management (signers, signerList, threshold, totalWeight)
- All proposals now vault-scoped (createProposal requires vaultId)
- Initialize() simplified (no initial signers, create vault 0 manually after deploy)
- Remove legacy functions (addSigner without vaultId, updateSignerWeight, etc)
- Proposal struct includes vaultId field
- Approvals check vault-specific signer permissions

Storage layout optimized with 50 reserved slots for future upgrades.
Enhance deployment workflows to automatically extract and save contract addresses after deployment.

Changes:
- Parse deployment logs to extract proxy and implementation addresses
- Generate deployments/{network}.json with contract addresses and metadata
- Auto-commit deployment files back to repository
- Supports both Base Sepolia and Base Mainnet networks

This eliminates manual address tracking and ensures deployment history is preserved in the repository.
Add comprehensive vault lifecycle management:
- Vault archival functionality for disabling vaults
- Signer weight updates for mid-life configuration changes
- Improved storage layout documentation for UUPS upgradeability
- Clear separation of vault-level vs controller-level data

Technical changes:
- Add archiveVault() to permanently disable vaults
- Add updateSignerWeight() to modify signer voting power
- Fix OpenZeppelin v4.9.6 compatibility (__Ownable_init parameter)
- Add VaultSignerWeightUpdated and VaultArchived events
- Document storage slots for upgrade safety
New test coverage:
- VaultLifecycle.t.sol: Tests for vault creation, archival, independence
- ConfigurationChanges.t.sol: Tests for mid-life configuration changes

Test scenarios:
- Vault archival and reactivation restrictions
- Signer weight updates and their effects on proposals
- Threshold changes with active proposals
- Removing signers who have voted on active proposals
- Cross-vault configuration independence
- Edge cases for signer and threshold management

Removes outdated tests that used pre-multi-vault API.
All 42 tests pass successfully.
Create VAULT_MODEL.md specification document covering:

Architecture:
- Vault lifecycle (active → archived states)
- Data model separation (vault-level vs controller-level)
- Storage layout for UUPS upgradeability

Configuration Management:
- Mid-life configuration change rules and effects
- Threshold and weight update behaviors
- Edge cases and constraints

Integration Guidelines:
- API reference for integrators
- Event monitoring patterns
- Security considerations for auditors

Target audience: protocol integrators, auditors, and advanced users.
Links added to README for discoverability.
Pin dependencies to specific versions:
- forge-std@v1.12.0
- openzeppelin-contracts@v4.9.6
- openzeppelin-contracts-upgradeable@v4.9.6

Ensures ReentrancyGuardUpgradeable is available at expected path.
Update GitHub Actions workflows to use git submodules instead of
cloning wrong OpenZeppelin versions:
- Changed from v5.0.2 to v4.9.6 (via submodules)
- Enable recursive submodule checkout
- Remove manual dependency cloning
- Add deployment info script for post-upgrade verification

This ensures workflows use the same dependency versions as local dev.
Upgrade completed successfully:
- New implementation: 0xF3eD7F568dAe09536768081a4BFcf44E55703529
- Proxy remains: 0xB4FD2402c97c0F2E38B3Be91596aDe8927A36439
- Gas used: 3,399,951
- Verified on Basescan

New features available:
- archiveVault() for permanent vault deactivation
- updateSignerWeight() for mid-life signer weight changes
Production upgrade completed successfully:
- New implementation: 0xe85061dDf1Df886A07C71a94f4BB76DB54EC9631
- Proxy remains: 0xB4FD2402c97c0F2E38B3Be91596aDe8927A36439
- Gas used: 3,399,951
- Verified on Basescan

New production features:
- archiveVault() - permanent vault deactivation
- updateSignerWeight() - mid-life signer weight updates

Both Base Sepolia and Mainnet now running upgraded implementation.
Replace OwnableUpgradeable with AccessControlUpgradeable in core contracts.
Add GLOBAL_ADMIN, VAULT_ADMIN (per-vault), OPERATOR, and PAUSER roles.
Implement granular permissions with proper hierarchy enforcement.
Add emergency pause mechanism using PausableUpgradeable.

- MultiVault: role checks for vault creation, configuration, and proposals
- PayoutExecutor: role checks for payout operations
- Vault admins can only modify their assigned vaults
- Emergency pause blocks all state-changing operations
- Adjust storage __gap to accommodate new pause state
Add 21 tests for role assignment, privilege escalation protection, and cross-vault isolation.
Add 21 tests for emergency pause mechanism covering all operations.

Role system tests verify:
- DEFAULT_ADMIN, VAULT_ADMIN, OPERATOR, PAUSER role assignment
- Role hierarchy enforcement (operators cannot grant roles)
- Cross-vault isolation (vault admins cannot modify other vaults)
- Privilege escalation prevention

Emergency pause tests verify:
- Only GLOBAL_ADMIN and PAUSER can pause/unpause
- All state-changing operations blocked when paused
- View functions remain accessible
- Resume operations after unpause
Create comprehensive documentation for role-based access control:
- Role hierarchy and access control matrix
- Privilege escalation protection mechanisms
- Emergency pause behavior and recovery procedures

Add governance pattern guides for different organization types:
- DAO treasury management pattern
- Company treasury management pattern
- Hybrid foundation + DAO pattern
- Multi-project allocation pattern

Include implementation examples, security considerations, and operational workflows for each pattern.
Create UpgradeRoles.s.sol for deploying new implementations with RBAC.
Update UpdateDeployment.s.sol to work with AccessControl (remove owner() call).

Upgrade script handles:
- Deploy new MultiVault implementation
- Deploy new PayoutExecutor implementation
- Upgrade proxies via UUPS
- Preserve existing storage and vault data
Add links to new documentation:
- Role System Design
- Governance Patterns

Update key features section:
- Role-Based Access Control
- Emergency Pause circuit breaker
- Enhanced security features
Add reinitializer functions to support migration from Ownable to
AccessControl during contract upgrades. Without this, upgraded contracts
would become unmanageable as no address would have DEFAULT_ADMIN_ROLE.

Changes:
- Add initializeV2() with reinitializer(2) to MultiVault.sol
- Add initializeV2() with reinitializer(2) to PayoutExecutor.sol
- Update Upgrade.s.sol to call initializeV2() during upgrade
- Update UpgradeRoles.s.sol to call initializeV2() for both contracts

This ensures smooth migration from v1 (Ownable) to v2 (AccessControl)
while preserving admin access during UUPS proxy upgrades.

All 84 tests passing.
Modified upgrade workflows to automatically read proxy addresses from
deployments/*.json files instead of requiring manual input.

Changes:
- Remove proxy_address input parameter from workflows
- Add step to read addresses from deployment JSON using jq
- Use MULTIVAULT_PROXY and EXECUTOR_PROXY from deployment files
- Update upgrade log to use auto-detected proxy address

Benefits:
- No manual address input required
- Single source of truth for deployment addresses
- Prevents human error in address specification
- Faster workflow execution
Add diagnostic scripts to verify deployer address and ownership before
running upgrade scripts.

Changes:
- script/CheckOwner.s.sol: Check if deployer is owner of proxy
- script/ShowDeployer.s.sol: Display deployer address from private key

These utilities help diagnose "caller is not the owner" errors during
contract upgrades.
The previous approach cast the proxy to the new MultiVault type which
uses AccessControl, causing ABI conflicts when calling upgradeToAndCall
on proxies still using the old Ownable-based implementation.

Changes:
- Add IUUPSProxy interface to both upgrade scripts
- Cast proxy to IUUPSProxy instead of MultiVault/PayoutExecutor
- This avoids ABI mismatches between old and new implementations

This fixes "Ownable: caller is not the owner" errors during upgrades
caused by improper function selector resolution.
Add CheckCurrentState script to diagnose ownership and access control
state before attempting upgrades. This helps identify if:
- Contract uses Ownable (check owner())
- Contract uses AccessControl (check DEFAULT_ADMIN_ROLE)
- Owner is address(0) (unmanageable contract)

This diagnostic step will run before upgrade in workflows to catch
permission issues early and provide clear error messages.
- Updated test.yml to trigger on 'developing' branch
- Removed OpenZeppelin v5.0.2 installation steps from workflows
- Fixed deploy workflows to use submodules: recursive
- Removed all upgrade-related scripts and workflows
  - Deleted Upgrade.s.sol, UpgradeRoles.s.sol
  - Deleted CheckCurrentState.s.sol, CheckOwner.s.sol, ShowDeployer.s.sol
  - Deleted upgrade-base-sepolia.yml, upgrade-base-mainnet.yml

Rationale: Existing contracts on both Base Sepolia and Mainnet have
owner = address(0) and are unmanageable. Strategy pivot to deploy
fresh contracts with proper AccessControl role system.
Updated deployment files with fresh contract addresses:

Base Sepolia:
- MultiVault: 0xc8c7F4E139B10711f9a9cbf827De58437C001d37
- PayoutExecutor: 0xCCc16fC6e3cb9a8260689a15fd9687C857D6Ccf7

Base Mainnet:
- MultiVault: 0x183551dda7Aa336f8a08633E8EdF5562BC771371
- PayoutExecutor: 0x08fE6651A4835B1cBf26412f8bcbc04ffc15aC0a

Both deployments verified on BaseScan with DEFAULT_ADMIN_ROLE
properly assigned to deployer (0xBd53dA0eb0B80061dbF942D8CE98EfC4Dc0e0635).

Cleaned repository:
- Removed old upgrade-log.txt
- Updated deployment JSON files with AccessControl metadata
- Repository ready for production use
@nikitavorobie
nikitavorobie merged commit 550f0f8 into main Dec 12, 2025
1 check passed
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