Skip to content

Latest commit

 

History

History
251 lines (206 loc) · 11.1 KB

File metadata and controls

251 lines (206 loc) · 11.1 KB

Implementation Guide: Governance Parameter Changes (#1038)

Overview

This guide outlines the implementation steps for routing protocol parameter changes through governance instead of per-contract admin timelocks.

Completed Work

1. Parameter Inventory and Classification ✅

  • File: governance_parameter_inventory.md
  • Content: Complete inventory of all admin-settable parameters across pool, invoice, oracle_registry, and compliance contracts

Phase 2: Governance Contract Updates

  • Added GovernanceAction enum with typed variants for all governance-gated parameters
  • Updated Proposal struct to use GovernanceAction instead of generic function_name/calldata
  • Added cross-contract client traits for pool, invoice, oracle_registry, and compliance
  • Implemented execute_governance_action() to dispatch all governance actions
  • Added governance address storage, bootstrap, and getter functions
  • Added GovernanceNotConfigured error and require_governance() helper

Phase 3: Target Contract Updates

  • Pool Contract: Added 28 *_via_governance entrypoints
  • Invoice Contract: Added 19 *_via_governance entrypoints
  • Oracle Registry Contract: Added 4 *_via_governance entrypoints
  • Compliance Contract: Added 2 *_via_governance entrypoints

Phase 4: SDK Updates

  • Updated packages/sdk/src/generated/governance.ts with new types:
    • Added GovernanceNotConfigured error
    • Added ProposalStatus, ProposalCategory enums
    • Added LoyaltyTier, FeeTier, CollateralConfig, QuorumTier interfaces
    • Added GovernanceAction discriminated union with all action variants
    • Added Proposal and GovernanceConfig interfaces
  • Created packages/sdk/src/clients/governance.ts client with:
    • initialize() - Bootstrap governance contract
    • createProposal() - Create governance proposals with typed actions
    • vote() - Vote on proposals
    • executeProposal() - Execute passed proposals
    • cancelProposal() - Cancel proposals
    • getProposal() / getAllProposals() - Query proposals
    • getConfig() - Get governance configuration
    • getGovernanceAddress() - Get governance address from target contracts
    • governanceActionToScVal() - Convert TypeScript actions to Soroban ScVal
  • Updated packages/sdk/src/index.ts to export governance client and types

Phase 5: Frontend Updates

  • Added "Governance" link to admin navigation in frontend/components/AdminNav.tsx
  • Created frontend/app/admin/governance/page.tsx with:
    • Proposal listing with status badges and category tags
    • Voting interface (for/against) for active proposals
    • Execution button for passed proposals
    • Vote progress visualization with for/against bars
    • Quorum and pass threshold indicators
    • Stats cards showing total, active, passed, and executed proposals
    • Create proposal modal (placeholder - requires governance contract deployment)
    • Action description formatting for all governance action types
  • Added governance translations to frontend/locales/en/common.json:
    • Navigation label
    • All UI text for proposal listing, voting, and execution
    • Status and category labels
    • Action descriptions for common parameter changes

Phase 6: Test Updates

  • Created contracts/governance/tests/governance_flow_tests.rs with comprehensive integration tests:
    • Basic governance flow tests (create proposal, vote, execute after timelock)
    • Pool parameter change tests (yield, treasury, fee tier, collateral config)
    • Invoice parameter change tests (grace period, max amount)
    • Oracle registry parameter change tests (invoice contract, quorum tiers)
    • Compliance parameter change tests (rescreening interval, screener timelock)
    • Governance gating tests (reject non-governance callers, require governance address)
    • Quorum and pass threshold tests (reject when not met)
    • Timelock tests (cannot execute before timelock expires)

Summary

All governance parameter change refactoring tasks have been completed:

Phase 1: Parameter inventory and classification
Phase 2: Governance contract updates with typed actions
Phase 3: Target contract governance-gated entrypoints (53 total across 4 contracts)
Phase 4: SDK updates with governance client and types
Phase 5: Frontend admin UI for governance proposals
Phase 6: Integration tests for full governance flow

The protocol now has a centralized governance system that routes all governance-gated parameter changes through the governance contract with proposal, vote, timelock, and execute safeguards.

Pattern for *_via_governance entrypoints:

pub fn set_yield_via_governance(
    env: Env,
    governance: Address,
    new_yield_bps: u32,
) -> Result<(), PoolError> {
    governance.require_auth();
    Self::require_governance(&env, &governance)?;
    // ... existing setter logic ...
    Ok(())
}

Invoice Contract

File: contracts/invoice/src/lib.rs

Required Changes:

  1. Add GOVERNANCE Symbol key
  2. Add require_governance() helper function
  3. Add *_via_governance entrypoints for all governance-gated parameters (19 parameters)
  4. Add set_governance_address() bootstrap entrypoint
  5. Add get_governance_address() getter
  6. Add GovernanceNotConfigured error variant to InvoiceError

Oracle Registry Contract

File: contracts/oracle_registry/src/lib.rs

Required Changes:

  1. Add GOVERNANCE Symbol key
  2. Add require_governance() helper function
  3. Add *_via_governance entrypoints for all governance-gated parameters (4 parameters)
  4. Add set_governance_address() bootstrap entrypoint
  5. Add get_governance_address() getter
  6. Add GovernanceNotConfigured error variant to OracleRegistryError

Compliance Contract

File: contracts/compliance/src/lib.rs

Required Changes:

  1. Add GOVERNANCE Symbol key
  2. Add require_governance() helper function
  3. Add *_via_governance entrypoints for all governance-gated parameters (2 parameters)
  4. Add set_governance_address() bootstrap entrypoint
  5. Add get_governance_address() getter
  6. Add GovernanceNotConfigured error variant to ComplianceError

Phase 2: Complete Governance Action Implementation

File: contracts/governance/src/lib.rs

Remaining Work:

  1. Complete all client trait methods (currently only a few are implemented)
  2. Implement all remaining GovernanceAction variants in execute_governance_action()
  3. Add proper error handling for cross-contract call failures

Phase 3: SDK Updates

Directory: packages/sdk/sdk/

Required Changes:

  1. Update governance client to use new create_proposal signature with GovernanceAction
  2. Add helper functions for creating parameter change proposals:
    • create_yield_change_proposal()
    • create_grace_period_proposal()
    • etc. (one helper per common parameter change)
  3. Update type generation to include new GovernanceAction enum

Phase 4: Frontend Updates

Directory: frontend/app/admin/*

Required Changes:

  1. Update parameter change forms to create governance proposals instead of direct admin calls
  2. Add governance proposal status tracking UI
  3. Keep emergency pause as direct action (fast path)
  4. Update parameter change confirmation dialogs to show proposal flow
  5. Add proposal history view for parameter changes

Phase 5: Test Updates

Directory: contracts/*/tests/

Required Changes:

  1. Update existing tests that call admin setters directly to use governance flow
  2. Add test-only fast paths clearly marked as #[cfg(test)]
  3. Add integration tests for full proposal → vote → timelock → execute cycle
  4. Test at least one parameter per affected contract through full governance cycle
  5. Test emergency pause path independently
  6. Test governance address bootstrap and rotation

Testing Strategy

Unit Tests

  • Test each *_via_governance entrypoint with proper governance verification
  • Test rejection when governance address not configured
  • Test rejection when caller is not governance contract
  • Test parameter validation still works via governance path

Integration Tests

  • Test full governance cycle:
    1. Bootstrap governance address on target contract
    2. Create proposal with GovernanceAction
    3. Vote on proposal
    4. Wait for voting period + timelock
    5. Execute proposal
    6. Verify parameter changed on target contract
  • Test emergency pause independently
  • Test governance address rotation

Test-Only Fast Paths

Add test helpers that bypass governance for unit tests:

#[cfg(test)]
fn test_set_yield_directly(env: &Env, new_yield: u32) {
    // Direct setter for testing only
}

Deployment Strategy

  1. Deploy updated governance contract with new GovernanceAction enum and client traits
  2. Update target contracts with *_via_governance entrypoints
  3. Bootstrap governance relationship by calling set_governance_address() on each target contract
  4. Gradual migration: Keep existing admin setters for backward compatibility, mark as deprecated
  5. Update SDK and frontend to use new governance flow
  6. Monitor and deprecate: After successful migration, consider removing or restricting direct admin setters

Rollback Plan

If issues arise:

  1. Governance-gated entrypoints are additive - existing admin setters still work
  2. Can revert to old flow by not calling set_governance_address()
  3. Access control multisig path remains independent and unaffected

Security Considerations

  1. Governance address bootstrap: Only callable by admin, one-time setup per contract
  2. Self-rotation: Governance contract can rotate its own address via set_governance_address() if needed
  3. Emergency pause: Remains fast via admin or access_control multisig
  4. Timelock: Governance proposals still respect existing timelock (48 hours default)
  5. Quorum tiers: Parameter changes use lower quorum (10%) than critical actions (50%)

Acceptance Criteria Checklist

  • Every parameter classified as "governance-gated" can only be changed via executed governance proposal
  • Emergency-pause path remains fast and independently testable
  • Full proposal → vote → timelock → execute → parameter-changed cycle covered by integration test
  • Admin frontend reflects new flow with no dead UI pointing at removed direct-setter entrypoints
  • All governance-gated parameters have corresponding *_via_governance entrypoints
  • Governance contract can execute all defined GovernanceAction variants
  • SDK updated for new governance flow
  • Frontend admin pages updated for governance proposal UI
  • Contract test suites updated to use new governance flow
  • Integration tests added for full governance cycle

Next Steps

  1. Implement Phase 1: Add *_via_governance entrypoints to all four contracts
  2. Complete Phase 2: Finish implementing all GovernanceAction variants in governance contract
  3. Test Phase 1-2: Write unit tests for new entrypoints
  4. Implement Phase 3: Update SDK
  5. Implement Phase 4: Update frontend
  6. Implement Phase 5: Update all contract tests
  7. Integration testing: Add full governance cycle tests
  8. Documentation: Update deployment guides and user documentation