A comprehensive treasury governance smart contract built with Ink! v6 for Polkadot/Substrate chains.
The contract is modularized into separate files for better organization and maintainability:
governance/
├── lib.rs # Main contract implementation
├── errors.rs # Error types and Result type alias
├── events.rs # Contract events
├── structs.rs # Data structures (enums and structs)
└── Cargo.toml # Project dependencies
Main contract file that includes:
- Contract storage structure
- Constructor and initialization
- All message functions (create_proposal, vote, execute_proposal, etc.)
- Query functions
- Unit tests
The file uses include! macro to inline the separate module files as required by Ink! v6.
Contains all custom error types:
Errorenum with all possible contract errorsResult<T>type alias for cleaner error handling
Defines all contract events:
ProposalCreated- Emitted when a new proposal is createdVoteCast- Emitted when a vote is castProposalExecuted- Emitted when a proposal is executedProposalStatusChanged- Emitted when proposal status changesProposalCancelled- Emitted when a proposal is cancelled
Contains all data structures:
- Enums:
ProposalType,VotingPeriod,QuorumThreshold,ExecutionDelay,ProposalStatus - Structs:
GovernanceParameters,VotingOptions,VoteChoice,Proposal,Vote - Helper methods for conversions (e.g.,
to_blocks(),to_percentage())
This contract uses Ink! v6 with H160 address type (Ethereum-style addresses).
-
Proposal Management
- Create proposals with custom governance parameters
- Support for different proposal types (Treasury, Governance, Technical, Other)
- Flexible voting options (1-10 options per proposal)
-
Voting System
- One vote per address
- Prevention of double voting
- Configurable voting periods (3, 7, 14, or 30 days)
- Real-time vote counting
-
Governance Parameters
- Voting Period: Duration for voting on proposals
- Quorum Threshold: Minimum participation required (5%, 10%, 20%, or 25%)
- Execution Delay: Time delay before execution (Immediate, 1, 2, or 7 days)
-
Proposal Execution
- Automatic status updates based on quorum and voting results
- Tie detection (proposals rejected on tie)
- Execution delay enforcement
-
Voter Registration
- Register voters for accurate quorum calculations
- Track total number of registered voters
- Rust toolchain
- cargo-contract CLI tool
cargo contract build --releasecargo testcargo contract instantiate --suri //Alicenew()- Initialize the contract
register_voter()- Register as a voterget_total_voters()- Get total registered votersis_registered_voter(account)- Check if an address is registered
create_proposal(...)- Create a new proposalcancel_proposal(proposal_id)- Cancel an active proposal (owner/proposer only)get_proposal(proposal_id)- Get proposal detailsget_all_proposal_ids()- Get all proposal IDs
vote(proposal_id, option_index)- Cast a voteget_user_vote(proposal_id, user)- Get a user's voteupdate_proposal_status(proposal_id)- Update proposal status after voting ends
execute_proposal(proposal_id)- Execute a passed proposal
get_stats()- Get contract statisticshas_reached_quorum(proposal_id)- Check if quorum is reachedget_proposal_results(proposal_id)- Get vote counts and quorum statusget_voting_options(proposal_id)- Get voting optionsget_detailed_results(proposal_id)- Get results with option namesget_winning_option(proposal_id)- Get winning option and vote countget_owner()- Get contract owner address
- Authorization Checks: Only owner or proposer can cancel proposals
- Double Voting Prevention: Each address can only vote once per proposal
- Overflow Protection: Uses checked arithmetic for all calculations
- Status Validation: Ensures proposals are in correct state before actions
- Time-based Controls: Enforces voting periods and execution delays
// 1. Register as a voter
contract.register_voter()?;
// 2. Create a proposal
let governance_params = GovernanceParameters {
voting_period: VotingPeriod::SevenDays,
quorum_threshold: QuorumThreshold::Ten,
execution_delay: ExecutionDelay::OneDay,
};
let voting_options = VotingOptions {
options: vec![
String::from("Approve"),
String::from("Reject"),
],
};
let proposal_id = contract.create_proposal(
String::from("Treasury Allocation"),
String::from("Allocate 100 tokens for development"),
ProposalType::Treasury,
governance_params,
voting_options,
)?;
// 3. Vote on the proposal
contract.vote(proposal_id, 0)?; // Vote for option 0 (Approve)
// 4. Update proposal status (after voting period)
contract.update_proposal_status(proposal_id)?;
// 5. Execute proposal (after execution delay)
contract.execute_proposal(proposal_id)?;This project is licensed under MIT License.
Contributions are welcome! Please ensure all tests pass before submitting a PR.