Skip to main content

Contracts

The contracts/ directory contains Foundry/Solidity smart contracts implementing on-chain governance, a dapp registry, and build constraints storage. Built on OpenZeppelin 5.4.

Contract Inventory

VfiToken (21 lines)

ERC20 + ERC20Votes. Constructor mints initial supply to a designated holder. Holders must self-delegate to activate voting power.

VfiGovernor (176 lines)

OpenZeppelin Governor stack: GovernorSettings + GovernorCountingSimple (For/Against/Abstain) + GovernorVotes + GovernorVotesQuorumFraction + GovernorTimelockControl.

Extensions:

  • Pluggable proposal eligibility via IProposalRequirements. Governance can swap the implementation with setProposalRequirements().
  • Security Council veto: vetoProposal() cancels any active proposal. Council address updatable via setSecurityCouncil().

VfiTimelock (10 lines)

Thin wrapper around OZ TimelockController. Governor holds PROPOSER_ROLE, execution is open (address(0) has EXECUTOR_ROLE).

DappRegistry (176 lines)

Stores dapp versions on-chain. Each version holds a rootCid (bytes), status, proposer, and timestamp.

Version lifecycle:

Published ←→ Paused    (reversible, Council or governance)

Deprecated (terminal, cannot unpause)

Human-readable metadata (name, version, description) is emitted as DappMetadata events, not stored — gas efficient, indexer friendly.

Access control:

  • GOVERNANCE_ROLE (held by Timelock): publish, upgrade, deprecate
  • SECURITY_COUNCIL_ROLE: pause, unpause, deprecate

ConstraintsRegistry (31 lines)

Maps constraintsId (bytes32) → rootCid (bytes). Governance-only updates. Used by CLI and Client to anchor build constraints without on-chain policy logic.

MinimumDelegationRequirement (27 lines)

Default IProposalRequirements implementation. Requires proposer voting power >= minBps basis points of total supply (default: 100 BPS = 1%).

Governance Flow

Proposer (eligible) → propose()
↓ voting delay (blocks)
Voting period (token holders vote For/Against/Abstain)
↓ voting period ends
Queue to Timelock → queue()
↓ timelock delay (seconds)
Execute → execute() (anyone can call)

At any point before execution, the Security Council can vetoProposal() to cancel.

Deployment

Local Devnet

cd contracts && ./script/local-devnet.sh

Starts Anvil, deploys all contracts, writes .devnet/devnet.json with addresses, test accounts, and private keys.

Test accounts (from mnemonic "test test test...junk"):

IndexRoleAllocation
0Developer/deployerRemaining supply
1Voter 1100k VFI
2Voter 2100k VFI
3Security Council 150k VFI (assigned on-chain)
4Security Council 250k VFI (funded, not assigned)

Default devnet parameters (configurable via env vars):

ParameterDefault
VOTING_DELAY1 block
VOTING_PERIOD20 blocks
QUORUM_FRACTION4%
TIMELOCK_DELAY1 second
MIN_PROPOSAL_BPS100 (1%)
INITIAL_SUPPLY1M VFI

Sepolia

DeploySepolia.s.sol derives deployer from SEPOLIA_MNEMONIC, self-delegates, and outputs JSON with all addresses.

Build Profile

VfiGovernor exceeds EIP-170 contract size limits without optimization. Deployment requires the CI profile:

FOUNDRY_PROFILE=ci forge build --sizes
FOUNDRY_PROFILE=ci forge test -vvv

Testing

Two test suites:

  • DappRegistry.t.sol: Unit tests for publish, upgrade, pause/unpause
  • GovernanceIntegration.t.sol: Full governance cycles — propose, vote, queue, execute, veto, upgrade, deprecate, constraints update

Tests use DeployVibeFi.deploy() to mirror production deployment, plus Forge cheatcodes (vm.prank, vm.warp, vm.expectEmit).

Known Limitations

  1. Security Council rotation requires updating both VfiGovernor.setSecurityCouncil() AND manually granting roles on DappRegistry/ConstraintsRegistry. These are separate operations.
  2. Deprecated is terminal — cannot be reversed.
  3. Metadata is event-only — must be indexed off-chain to query.