Skip to main content

Governance Libraries

The governance libraries provide reusable primitives for time-delayed governance and safe admin handover. The package surface is intentionally minimal and focuses on the common pattern of proposing a new value, waiting a configured delay, and then accepting the proposal.

Overview

Contract Types: Abstract contract (Admin) and Struct library (ProposalStructs) License: EVVM-NONCOMMERCIAL-1.0 Import Paths:

  • @evvm/testnet-contracts/library/utils/governance/Admin.sol
  • @evvm/testnet-contracts/library/utils/governance/ProposalStructs.sol

Key Features

  • Time-delayed proposals for addresses and uint values
  • Simple boolean-flag proposals with delay
  • Built-in events to track proposals and acceptances
  • Small, audit-friendly surface

ProposalStructs

ProposalStructs exposes simple structs used by governance-enabled contracts:

struct AddressTypeProposal {
address current;
address proposal;
uint256 timeToAccept;
}

struct UintTypeProposal {
uint256 current;
uint256 proposal;
uint256 timeToAccept;
}

struct BoolTypeProposal {
bool flag;
uint256 timeToAcceptChange;
}

They are designed to store the current value, the proposed new value and the earliest timestamp when the proposal can be accepted.

Admin Contract

Admin is an abstract contract that uses the AddressTypeProposal pattern to manage admin handovers with a time delay.

Events

  • event AdminProposed(address indexed newAdmin, uint256 timeToAccept);
  • event AdminAccepted(address indexed newAdmin);

Errors

  • error SenderIsNotAdmin(); — thrown when a non-admin attempts to perform admin-only actions
  • error ProposalNotReady(); — thrown when attempting to accept a proposal before timeToAccept

Functions

proposeAdmin

function proposeAdmin(address newAdmin, uint256 delay) external onlyAdmin

Proposes a new admin; sets admin.proposal and admin.timeToAccept = block.timestamp + delay, emits AdminProposed

acceptAdmin

function acceptAdmin() external

Accepts the pending proposal; requires caller to be the proposed admin and block.timestamp >= admin.timeToAccept; sets admin.current = admin.proposal and clears the proposal fields; emits AdminAccepted

Modifier

  • onlyAdmin() — restricts execution to the currently active admin address

Usage Example

import {Admin} from "@evvm/testnet-contracts/library/utils/governance/Admin.sol";

contract MyService is Admin {
constructor(address initialAdmin) Admin(initialAdmin) {}

function proposeNewAdmin(address candidate, uint256 delay) external onlyAdmin {
proposeAdmin(candidate, delay);
}

function acceptNewAdmin() external {
acceptAdmin();
}

// Other admin-controlled functions
function updateServiceParameter(uint256 value) external onlyAdmin {
// Only current admin can call this
}
}

Usage in EVVM Contracts

Many EVVM contracts use the Admin base contract for governance:

  • TreasuryHostChainStation: Uses Admin for admin management
  • TreasuryExternalChainStation: Uses Admin for admin management
  • NameService: Uses Admin patterns
  • Staking: Uses Admin for governance
  • P2PSwap: Uses Admin patterns

Typical Pattern:

contract EVVMContract is Admin {
ProposalStructs.AddressTypeProposal public someAddress;

function proposeSomeAddress(address newAddress, uint256 delay) external onlyAdmin {
someAddress.proposal = newAddress;
someAddress.timeToAccept = block.timestamp + delay;
}

function acceptSomeAddress() external onlyAdmin {
require(block.timestamp >= someAddress.timeToAccept, "Too early");
someAddress.current = someAddress.proposal;
someAddress.proposal = address(0);
someAddress.timeToAccept = 0;
}
}

Best Practices

  1. Delay Duration: Use 1 day (86400 seconds) minimum for production contracts
  2. Proposal Validation: Check newAdmin != address(0) and newAdmin != admin.current
  3. Event Monitoring: Listen for AdminProposed and AdminAccepted events
  4. Multi-sig Recommended: Use multi-sig wallets for admin addresses

Recommendation: Inherit from Admin for any contract requiring admin governance with time-delayed changes.

Note: delay should be chosen according to governance risk model. For development and testing, short delays (minutes) are convenient. For production deployments consider multi-day delays.

Best Practices

  • Require onlyAdmin for functions that change critical configuration
  • Use events to record proposal and acceptance timestamps for off-chain monitoring
  • Keep proposals minimal (one field at a time) to simplify reasoning and audits

Tests

Add unit tests that:

  • Verify proposeAdmin() sets the proposal and emits AdminProposed
  • Verify acceptAdminProposal() reverts before timeToAccept and succeeds after
  • Verify onlyAdmin prevents unauthorized callers

See Also

  • EvvmService — services typically protect admin functions using AdminControlled or similar patterns
  • Governance processes — consider adding documentation of expected delay values for testnets vs production