Skip to content

feat(contracts): Build On-Chain Governance Contract with Timelock Executor, Snapshot Voting, and Cross-Contract Execution - #42

Merged
GoSTEAN merged 1 commit into
GruftNet:mainfrom
williamsokperez-arch:feat/governance-contract
Jun 20, 2026
Merged

feat(contracts): Build On-Chain Governance Contract with Timelock Executor, Snapshot Voting, and Cross-Contract Execution#42
GoSTEAN merged 1 commit into
GruftNet:mainfrom
williamsokperez-arch:feat/governance-contract

Conversation

@williamsokperez-arch

Copy link
Copy Markdown
Contributor

Summary

Implements the full on-chain governance stack for BOXMEOUT as described in issue #18.

  • Snapshot voting: voter's XLM balance is captured at vote() call time and stored immutably — subsequent balance changes have no effect
  • Voting period: 7 days = 120,960 ledgers (5s/ledger)
  • Quorum: (votes_for + votes_against) * 20 >= quorum_supply — ≥ 5% of total platform XLM must participate
  • Pass threshold: votes_for > votes_against AND quorum reached
  • Timelock: 48-hour delay (34,560 ledgers) between finalization and execution
  • Execution: dispatches cross-contract call to MarketFactory.set_fee_bps, MarketFactory.update_token_list, or Treasury.set_max_discount
  • Veto: admin-only before voting ends; sets 7-day cooldown (120,960 ledgers) per proposal-type variant

Files Changed

File Change
contracts/governance/Cargo.toml New crate
contracts/governance/src/lib.rs Full governance contract implementation
contracts/governance/src/tests.rs 24 Soroban tests covering all lifecycle paths
contracts/Cargo.toml Added governance to workspace members
contracts/SECURITY_AUDIT.md Governance reentrancy audit (FINDING-GOV-001 through 004)

Security (CEI / Reentrancy)

All state changes in execute() happen before the cross-contract call:

// EFFECTS — mark executed before cross-contract call (reentrancy guard)
proposal.status = ProposalStatus::Executed;
Self::save_proposal(&env, &proposal);

// INTERACTIONS — cross-contract calls come last
FactoryGovClient::new(&env, &factory).set_fee_bps(&bps);

Test Coverage (24 tests, all passing)

test tests::test_full_happy_path_fee_rate_execute_calls_factory ... ok
test tests::test_execute_max_discount_rate_calls_treasury ... ok
test tests::test_execute_add_token_calls_factory ... ok
test tests::test_execute_remove_token_calls_factory ... ok
test tests::test_failed_quorum_marks_proposal_failed ... ok
test tests::test_failed_threshold_when_against_wins ... ok
test tests::test_veto_before_voting_ends_sets_vetoed_status ... ok
test tests::test_double_vote_returns_already_voted ... ok
test tests::test_execute_before_timelock_expires_returns_error ... ok
test tests::test_snapshot_power_is_captured_at_vote_time_and_immutable ... ok
test tests::test_veto_cooldown_blocks_same_type_within_7_days ... ok
test tests::test_veto_cooldown_expires_after_7_days ... ok
... (12 more)

Interface Notes

MarketFactory and Treasury need to implement the governance callable functions:

// MarketFactory must add:
fn set_fee_bps(env: Env, fee_bps: u32)
fn update_token_list(env: Env, token: Address, add: bool)

// Treasury must add:
fn set_max_discount(env: Env, bps: u32)

Testnet Deployment

Testnet contract address: pending deployment — to be added before merge

Closes #18

🤖 Generated with Claude Code

…imelock, and cross-contract execution

Adds Governance contract with create_proposal, vote, finalize, execute,
and veto. Implements 7-day voting (120,960 ledgers), 48h timelock
(34,560 ledgers), 5% quorum check, and cross-contract MarketFactory /
Treasury calls on execution.

Security:
- CEI in execute(): status → Executed BEFORE cross-contract call
- require_auth() first in create_proposal, vote, veto
- extend_ttl() on every persistent write (TTL_EXTEND_TO = 604,800 ledgers)
- Veto cooldown (7 days) per proposal-type discriminant

Closes GruftNet#18

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@GoSTEAN
GoSTEAN merged commit d3f815d into GruftNet:main Jun 20, 2026
0 of 2 checks 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