owned this note
                
                
                     
                     owned this note
                
                
                     
                    
                
                
                     
                    
                
                
                     
                    
                        
                            
                            Published
                        
                        
                            
                                
                                Linked with GitHub
                            
                            
                                
                                
                            
                        
                     
                
            
            
                
                    
                    
                
                
                    
                
                
                
                    
                        
                    
                    
                    
                
                
                
                    
                
            
            
         
        
        # Governance Precompile — #8366
https://github.com/paritytech/polkadot-sdk/issues/8366
**Authors:** Eman Herawy & Lucas Grasso  
**Mentor:** Francisco Aguirre, Ankan Anurag — [_PBA Bounty Hunters_](https://hackmd.io/@ak0n/pba-nation)
**Polkadot Account**: `12Eaqm6uyCZr2F3g7brynnFejj8c4iQHSzVfasPSwMUpjfZP`
## Prelude
This issue is part of the OG Rust bounties, see https://ogrust.com/.
## Summary
This proposal requests funding to implement an EVM precompile that exposes Polkadot's OpenGov functionality directly to smart contracts. This precompile will allow EVM developers to submit referenda, vote, delegate, and query governance state fully on-chain, without relying on Substrate RPCs.
By enabling direct OpenGov interactions from Solidity, we unlock hybrid EVM–Substrate governance dApps, reducing off-chain dependencies, improving UX, and expanding Polkadot's developer base.
**Development Approach:** We propose a **minimal-first strategy** — implementing first the core functions that enable smart contracts to submit referenda, cast all vote types, manage delegation, and query governance state, while documenting the full interface for future extensions based on the learned lessons from the minimal implementation.
Development will proceed across **2 milestones** over **4 weeks**, delivering a Solidity interface, Rust implementation, comprehensive tests, gas benchmarks, and documentation.
**Total requested bounty:** `27000 USD` (including implementation, testing, and documentation). See [#Developer Milestones](#Development-Milestones).
## Motivation
In view of the upcoming Asset Hub migration, creating a governance precompile will enable smart contracts (and their users) to directly participate in on-chain decision making.
By exposing key governance functions through a contract-friendly interface, DAOs and other contract applications can integrate natively with Polkadot's governance system, each with their own internal voting mechanisms.
We expose **one precompile per component of governance** to allow for modularity in the runtimes. For example, not all substrate runtimes will choose to use conviction voting as their way of updating Polls.
## Deliverables
- Solidity Interfaces with their corresponding documentation.
- `referenda-precompiles` and `conviction-voting-precompiles` crates, including:
  - Precompile implementation
  - Benchmarks
  - Tests
  - The documented interfaces mentioned above
- Smart contracts that demonstrate the precompile usage.
## Development Strategy: Minimal-First Approach
### Phase 1 (This Proposal): Minimal Interface
We propose implementing **21 essential functions** that cover the essential smart contract governance operations:
**Core Capabilities:**
* Referenda Precompile:
    - Referendum submission (inline + lookup)
    - Decision Deposit submission
    - Metadata management
    - Deposit refunds
    - Essential queries
* Conviction Voting Precompile:
    - All voting types (standard, split, split-abstain)
    - Vote removal (essential for changing votes)
    - Delegation management (delegate + undelegate)
    - Deposit refunds
    - Essential queries
### Phase 2 (Future Consideration): Extended Interface & More crates
In this phase, we plan to extend the precompiles in the following manner:
- Advanced Queries
- Aditional Cleanup functions (e.g. `removeOtherVote()`)
- Additional metadata operations (e.g. `getMetadata()`)
- Developing the `bounties-precompiles` and `treasury-precompiles` crates if deemed neccesary
- Adapting to the "More tools for DAOs" - Kusama's [WFC#538]((https://kusama.subsquare.io/referenda/538)). This might include developing a `tracks-precompiles` crate.
- The development of a `preimage-precompiles` crate, so to keep the whole of submitting referenda on the smart-contract side.
**Why Minimal-First?**
This approach prioritizes delivering core functionality quickly while minimizing risk through a smaller audit surface and incremental testing. By starting with essential features, we can gather real feedback and iterate based on actual usage patterns rather than assumptions, ensuring future extensions address genuine needs.
We **intentionally excluded** the full solidity interfaces from this proposal but **the full interface is documented in the appendix for review and future planning, see [#Appendix-Full-Interface-Design](#Appendix-Full-Interface-Design)**.
## Proposed Minimal Solidity Interfaces
> Proposed interfaces are subject to refinement during implementation
### IReferenda
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
/// @dev The on-chain address of the Referenda precompile.
address constant REFERENDA_PRECOMPILE_ADDRESS = address(0xB0000);
/// @title Referenda Precompile Interface
/// @notice A low-level interface for interacting with `pallet_referenda`.
/// It forwards calls directly to the corresponding dispatchable functions,
/// providing access to referendum submission and management.
/// @dev Documentation:
/// @dev - OpenGov: https://wiki.polkadot.com/learn/learn-polkadot-opengov
/// @dev - SCALE codec: https://docs.polkadot.com/polkadot-protocol/parachain-basics/data-encoding
interface IReferenda {
	/// @notice When the referendum should be enacted.
	enum Timing {
		/// @custom:variant Enact at specific block number
		AtBlock,
		/// @custom:variant Enact after N blocks from approval
		AfterBlock
	}
	/// @notice Information about a referendum status.
	enum ReferendumStatus {
		/// @custom:variant /// Referendum has been submitted and has substatus defined by `OngoingPhase`.
		Ongoing,
		/// @custom:variant Referendum finished with approval. Submission deposit is held.
		Approved,
		/// @custom:variant Referendum finished with rejection. Submission deposit is held.
		Rejected,
		/// @custom:variant Referendum finished with cancellation. Submission deposit is held.
		Cancelled,
		/// @custom:variant Referendum finished and was never decided. Submission deposit is held.
		TimedOut,
		/// @custom:variant Referendum finished with a kill.
		Killed
	}
	/// @notice Sub-phases of an ongoing referendum.
	enum OngoingPhase {
		/// @custom:variant Referendum is waiting for decision deposit to be placed
		AwaitingDeposit,
		/// @custom:variant Decision deposit placed, preparing
		Preparing,
		/// @custom:variant Ready but waiting for track space
		Queued,
		/// @custom:variant Active voting period
		Deciding,
		/// @custom:variant Passing, in confirmation period
		Confirming
	}
	/// @notice Submit a referendum via preimage lookup (for large proposals).
	/// @dev Requires prior call to `pallet_preimage::note_preimage()`
	/// @param origin The SCALE-encoded `PalletsOrigin` origin of the proposal.
	/// @param hash The hash of the referendum info to be looked up.
	/// @param preimageLength The length of the preimage in bytes.
	/// @param timing When the referendum should be enacted as defined in the `Timing` enum.
	/// @param enactmentMoment If `timing` is `AtBlock`, the block number for enactment. If `timing` is `AfterBlock`, the number of blocks after which to enact.
	/// @return referendumIndex The index of the newly created referendum.
	function submitLookup(
		bytes calldata origin,
		bytes32 hash,
		uint32 preimageLength,
		Timing timing,
		uint32 enactmentMoment
	) external returns (uint32 referendumIndex);
	/// @notice Submit a referendum inline (for small proposals).
	/// @param origin The SCALE-encoded `PalletsOrigin` origin of the proposal.
	/// @param proposal The proposal call data to be submitted inline.
	/// @param timing When the referendum should be enacted as defined in the `Timing` enum.
	/// @param enactmentMoment If `timing` is `AtBlock`, the block number for enactment. If `timing` is `AfterBlock`, the number of blocks after which to enact.
	/// @return referendumIndex The index of the newly created referendum.
	function submitInline(
		bytes calldata origin,
		bytes calldata proposal,
		Timing timing,
		uint32 enactmentMoment
	) external returns (uint32 referendumIndex);
	/// @notice Place the decision deposit for a referendum.
	/// @param referendumIndex The index of the referendum for which to place the deposit.
	function placeDecisionDeposit(uint32 referendumIndex) external;
	/// @notice Set metadata for a referendum. Only callable by the referendum submitter.
	/// @param referendumIndex The index of the referendum for which to set metadata.
	/// @param metadataHash The hash of the metadata to associate with the referendum.
	function setMetadata(uint32 referendumIndex, bytes32 metadataHash) external;
	/// @notice Clear metadata for a referendum and refund the metadata deposit.
	/// @param referendumIndex The index of the referendum for which to clear metadata.
	function clearMetadata(uint32 referendumIndex) external;
	/// @notice Refund the submission deposit for a referendum.
	/// @param referendumIndex The index of the referendum for which to refund the deposit.
	/// @return refundAmount The amount refunded to the submitter.
	function refundSubmissionDeposit(
		uint32 referendumIndex
	) external returns (uint128 refundAmount);
	/// @notice Refund the decision deposit for a referendum.
	/// @param referendumIndex The index of the referendum for which to refund the deposit.
	/// @return refundAmount The amount refunded to the depositor.
	function refundDecisionDeposit(uint32 referendumIndex) external returns (uint128 refundAmount);
	/// @notice Get comprehensive referendum information
	/// @param referendumIndex The index of the referendum to query.
	/// @return exists Whether the referendum exists
	/// @return status Current status as defined in the `ReferendumStatus` enum
	/// @return ongoingPhase Sub-phase if status is Ongoing as defined in the `OngoingPhase` enum
	/// @return trackId The governance track ID
	/// @return proposalHash Hash of the proposal
	/// @return submissionDeposit Submission deposit amount
	/// @return decisionDeposit Decision deposit amount
	/// @return enactmentBlock Block number for execution (if approved)
	/// @return submissionBlock Block when referendum was submitted
	function getReferendumInfo(
		uint32 referendumIndex
	)
		external
		view
		returns (
			bool exists,
			ReferendumStatus status,
			OngoingPhase ongoingPhase,
			uint16 trackId,
			bytes32 proposalHash,
			uint128 submissionDeposit,
			uint128 decisionDeposit,
			uint32 enactmentBlock,
			uint32 submissionBlock
		);
	/// @notice Check if a referendum would pass if ended now
	/// @param referendumIndex The referendum index
	/// @return exists Whether the referendum exists
	/// @return passing Whether the referendum would pass if ended now
	function isReferendumPassing(
		uint32 referendumIndex
	) external view returns (bool exists, bool passing);
	/// @notice Get the decision deposit left to be placed for a referendum
	/// @param referendumIndex The index of the referendum to query.
	/// @return The decision deposit amount required
	function decisionDeposit(uint32 referendumIndex) external view returns (uint128);
	/// @notice Get the submission deposit amount required for submitting a referendum
	/// @return The submission deposit amount
	function submissionDeposit() external view returns (uint128);
}
```
#### Design Choices:
- `IReferenda` was designed to be as abstract as possible, in order to allow for modularity. It does not expose any function that deppends on other pallets (e.g. It could expose a `getReferendumTally` function, but in depends on Conviction Voting, as the `Tally` trait is defined there).
- `PalletsOrigin` is encoded as to easily allow for different Origins in different runtimes, and dynamic traits / origins, as in "Kusama [WFC#538](https://kusama.subsquare.io/referenda/538)", wont be a breaking change.
- We expose a `decisionDeposit(uint32 referendumIndex)` function that shows how much decision deposit is LEFT to be deposited as to allow for [Crowd sourced decisions deposits]((https://github.com/paritytech/polkadot-sdk/issues/6041)).
- We use a `exists` flag or a zero-sentinel for view functions that might revert.
- We do not include track-related info because if in a future dynamic tracks are implemented under a `pallet-tracks` crate, another precompile `track-precompiles` should include these functions there.
    ```solidity
    /// @notice Get track information
    /// @param track The track ID
    /// @return exists Whether the track exists
    /// @return maxDeciding Maximum concurrent deciding referenda
    /// @return decisionDeposit Required decision deposit
    /// @return preparePeriod Preparation period in blocks
    /// @return decisionPeriod Decision period in blocks
    /// @return confirmPeriod Confirmation period in blocks
    /// @return minEnactmentPeriod Minimum enactment period in blocks
    function getTrackInfo(
        uint16 track
    )
        external
        view
        returns (
            bool exists,
            uint32 maxDeciding,
            uint128 decisionDeposit,
            uint32 preparePeriod,
            uint32 decisionPeriod,
            uint32 confirmPeriod,
            uint32 minEnactmentPeriod
        );
    ```
    This function may be added if deemed neccesary.
#### Some important future considerations:
- After talking with [@olanod](https://github.com/olanod) about his [input](https://github.com/paritytech/polkadot-sdk/issues/8366#issuecomment-3381162712) on the issue, the interface may expose a `vote` function in the future that allows for DAO's to vote on their own referenda. The current interface and separation of concerns allows for this update to happen.
- Voting with signatures. This is commonly used in many Solidity contracts, but is it worth considering for this context?
### IConvictionVoting
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
/// @dev The on-chain address of the Conviction Voting precompile.
address constant CONVICTION_VOTING_PRECOMPILE_ADDRESS = address(0xC0000);
/// @title ConvictionVoting Precompile Interface
/// @notice A higher-level interface for interacting with `pallet_conviction_voting` and querying referendum tallies.
/// It forwards calls directly to the corresponding dispatchable functions,
/// providing access to conviction voting functionalities.
/// @dev Documentation:
/// @dev - Accounts on Asset Hub: https://docs.polkadot.com/polkadot-protocol/smart-contract-basics/accounts/#accounts-on-asset-hub-smart-contracts
/// @dev - OpenGov: https://wiki.polkadot.com/learn/learn-polkadot-opengov
/// @dev - Voting in OpenGov: https://wiki.polkadot.com/learn/learn-polkadot-opengov/#voting-on-a-referendum
interface IConvictionVoting {
	/// @notice A value denoting the strength of conviction of a vote.
	enum Conviction {
		/// @custom:variant 0.1x votes, unlocked.
		None,
		/// @custom:variant 1x votes, locked for an enactment period following a successful vote.
		Locked1x,
		/// @custom:variant 2x votes, locked for 2x enactment periods following a successful vote.
		Locked2x,
		/// @custom:variant 3x votes, locked for 4x...
		Locked3x,
		/// @custom:variant 4x votes, locked for 8x...
		Locked4x,
		/// @custom:variant 5x votes, locked for 16x...
		Locked5x,
		/// @custom:variant 6x votes, locked for 32x...
		Locked6x
	}
	/// @notice The type of vote cast.
	enum VotingType {
		/// @custom:variant A standard vote, one-way (approve or reject) with a given amount of conviction.
		Standard,
		/// @custom:variant A split vote with balances given for both ways, and with no conviction.
		Split,
		/// @custom:variant A split vote with balances given for both ways as well as abstentions, and with no conviction.
		SplitAbstain
	}
	/// @notice Cast a standard vote (aye or nay) with conviction.
	/// @param referendumIndex The index of the referendum to vote on.
	/// @param aye True for approving, false for rejecting.
	/// @param conviction Conviction level as defined in the `Conviction` enum.
	/// @param balance The amount of tokens to vote with.
	function voteStandard(
		uint32 referendumIndex,
		bool aye,
		Conviction conviction,
		uint128 balance
	) external;
	/// @notice Cast a split vote with explicit aye and nay balances, no conviction/lock applied.
	/// @param referendumIndex The index of the referendum to vote on.
	/// @param ayeAmount Balance allocated to aye.
	/// @param nayAmount Balance allocated to nay.
	function voteSplit(uint32 referendumIndex, uint128 ayeAmount, uint128 nayAmount) external;
	/// @notice Cast a split vote with explicit aye, nay and abstain balances, no conviction/lock applied.
	/// @param referendumIndex The index of the referendum to vote on.
	/// @param ayeAmount Balance allocated to aye.
	/// @param nayAmount Balance allocated to nay.
	/// @param abstainAmount Balance allocated to abstain.
	function voteSplitAbstain(
		uint32 referendumIndex,
		uint128 ayeAmount,
		uint128 nayAmount,
		uint128 abstainAmount
	) external;
	/// @notice Remove a vote from a referendum.
	/// @param trackId The governance track identifier.
	/// @param referendumIndex The referendum index.
	function removeVote(uint16 trackId, uint32 referendumIndex) external;
	/// @notice Delegate voting power to another account within a specific governance track.
	/// @dev Applies the sender’s balance with the specified conviction multiplier.
	/// @param trackId The governance track identifier.
	/// @param to The account to which voting power is delegated. See "Accounts on Asset Hub" documentation for more information.
	/// @param conviction Conviction level as defined in the `Conviction` enum.
	function delegate(uint16 trackId, address to, Conviction conviction, uint128 balance) external;
	/// @notice Remove any existing delegation within a governance track.
	/// @param trackId The governance track identifier.
	function undelegate(uint16 trackId) external;
	/// @notice Unlock expired voting/delegation lock
	/// @param trackId The trackId/track ID to unlock
	/// @param target The account to unlock (can be yourself or others)
	function unlock(uint16 trackId, address target) external;
	/// @notice Get the current vote details for specific referendum of an account in a governance track
	/// @param who The account to query
	/// @param trackId The governance track to query
	/// @param referendumIndex The referendum index to query
	/// @return exists Whether a vote exists
	/// @return votingType The type of vote as defined in the `VotingType` enum.
	/// @return aye True if a standard vote is aye, false if nay. False for split and split-abstain votes.
	/// @return ayeAmount The amount of tokens voting aye (pre-conviction). 0 for standard nay votes.
	/// @return nayAmount The amount of tokens voting nay (pre-conviction). 0 for standard aye votes.
	/// @return abstainAmount The amount of tokens voting abstain (pre-conviction). 0 for standard and split votes.
	/// @return conviction The conviction level applied to the vote as defined in the `Conviction` enum. Not applicable for split and split-abstain votes.
	function getVoting(
		address who,
		uint16 trackId,
		uint32 referendumIndex
	)
		external
		view
		returns (
			bool exists,
			VotingType votingType,
			bool aye,
			uint128 ayeAmount,
			uint128 nayAmount,
			uint128 abstainAmount,
			Conviction conviction
		);
	/// @notice Get the current delegation details for an account in a governance track.
	/// @dev Returns zero values if no delegation.
	/// @param who The account to query
	/// @param trackId The governance track to query
	/// @return target The account to which voting power is delegated. See "Accounts on Asset Hub" documentation for more information.
	/// @return balance The amount of tokens delegated (pre-conviction).
	/// @return conviction The conviction level applied to the delegation as defined in the `Conviction` enum.
	function getDelegation(
		address who,
		uint16 trackId
	) external view returns (address target, uint128 balance, Conviction conviction);
	/// @notice Get voting tally for an ongoing referendum
	/// @param referendumIndex The index of the referendum to query.
	/// @return exists Whether referendum exists and is ongoing
	/// @return ayes Aye votes (post-conviction)
	/// @return nays Nay votes (post-conviction)
	/// @return support Aye votes (pre-conviction, for turnout calculation)
	function getReferendumTally(
		uint32 referendumIndex
	) external view returns (bool exists, uint128 ayes, uint128 nays, uint128 support);
}
```
#### Design choices:
- `IConvictionVoting` is a more high-level interface. It exposes core queries and write functions, and functions whose definition relies on this pallet, such as `getReferendumTally()`.
- We use a `exists` flag or a zero-sentinel for view functions that might revert.
- Anyone who wants to be delegated votes would need to map their Substrate account to an EVM address.
## Development Milestones
We estimate the time duration of this project to be **270 hours**.
With a price of 100USD per hour, we estimate the costs of this project to be 27000 USD.
Each milestone will include the respective function implementation, alongisde tests and benchmarks.
### Milestone 0: Interface Design
**Duration:** 80 Hours
**Budget:** 8000 USD
Already Done! This work is what you are seeing in our proposal.
### Milestone 1: Referenda Submission, Basic Voting and delegation
**Duration:** 100 hours  
**Budget:** 10000 USD
**IReferenda Deliverables:**
1. `submitLookup()` - Submit referendum via preimage hash
2. `submitInline()` - Submit small proposals inline
3. `placeDecisionDeposit()` - Fund decision deposit
4. `decisionDeposit()` - Query amount left to be deposited
5. `submissionDeposit()` - Query required deposit amount
**IConvictionVoting Deliverables:**
1. `voteStandard()` - Standard conviction voting (aye/nay with conviction)
7. `voteSplit()` - Split voting (aye/nay without conviction)
8. `voteSplitAbstain()` - Three-way split voting with abstention
9. `getVoting()` - Query vote details for specific referendum
10. `delegate()` - Delegate voting power to another account
11. `undelegate()` - Remove delegation
### Milestone 2: Vote and Lock Management, refunds and More Queries
**Duration:** 90 hours  
**Budget:** 9000 USD
**IConvictionVoting Deliverables:**
1. `removeVote()` - Remove/change votes
13. `getDelegation()` - Query delegation status
14. `unlock()` - Unlock expired voting/delegation locks
15. `getReferendumTally()` - Get vote counts (ayes, nays, support)
**IReferenda Deliverables:**
1. `getReferendumInfo()` - Get comprehensive referendum data (status, track, deposits, etc.)
17. `isReferendumPassing()` - Check if referendum would currently pass
18. `setMetadata()` - Set referendum metadata (IPFS hash)
19. `clearMetadata()` - Clear referendum metadata and refund deposit
20. `refundSubmissionDeposit()` - Refund submission deposit after referendum completion
21. `refundDecisionDeposit()` - Refund decision deposit after referendum completion
## What's Excluded (Can Be Added in Phase 2)
The following functions are **intentionally excluded** from this proposal but documented for future consideration:
**Advanced Queries (4 functions):**
- `getLockedBalance()` - Query locked balance for a track
- `getTotalLocked()` - Query max locked balance across all tracks
- `getDecidingStatus()` - Get detailed deciding phase info
- `getTrackInfo()` - Query track configuration parameters
**Vote Cleanup (1 functions):**
- `removeOtherVote()` - Remove someone else's expired vote (governance operation)
**Metadata (1 function):**
- `getMetadata()` - Query metadata hash
- **Note:** Metadata is UI-layer concern, rarely used by contracts
**Rationale for Exclusion:**
- These are **convenience functions**, not core governance operations
- All excluded functionality is available via direct extrinsic calls
- Contracts need **time-sensitive operations** in precompiles; everything else can wait
- Keeps audit surface small and focused
- Enables faster delivery and iteration
## Some usage examples
### CrowdSourcing for decision deposits:
This interface partially addresses [this](https://github.com/paritytech/polkadot-sdk/issues/6041) issue, allowing a method for crowdsourcing decision deposits.
Here's a simple contract that pools funds to place a decision deposit:
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
import "./IReferenda.sol";
enum Status {
        Depositing,
        Deposited,
        Withdrawing
}
/** 
 * @title CrowdSourcer
 * @dev Implements CrowdSourcing for decision deposits. Accepts native tokens and exposes a function that places decision deposit.
 */
contract CrowdSourcer {
    Status public status;
    uint32 public immutable referendumIndex;
    uint128 public immutable targetDecisionDeposit;
    uint128 public totalContributed;
    mapping(address => uint128) public contributors;
    
    constructor(
        uint32 _referendumIndex,
        uint128 _targetDecisionDeposit
    ) {
        referendumIndex = _referendumIndex;
        targetDecisionDeposit = _targetDecisionDeposit;
        status = Status.Depositing;
    }
    function placeDecisionDeposit() external {
        require(totalContributed >= targetDecisionDeposit, "CrowdSourcer: Target not reached");
        require(status == Status.Depositing, "CrowdSourcer: Already deposited");
        status = Status.Deposited;
        IReferenda(REFERENDA_PRECOMPILE_ADDRESS).placeDecisionDeposit(referendumIndex);
    }
    
    function refundDecisionDeposit() external {
        require(status == Status.Deposited, "CrowdSourcer: Not deposited");
        status = Status.Withdrawing; 
        IReferenda(REFERENDA_PRECOMPILE_ADDRESS).refundDecisionDeposit(referendumIndex);
    }
    
    function withdraw() external {
        require(status == Status.Withdrawing, "CrowdSourcer: Decision Deposit not refunded");
        uint128 amount = contributors[msg.sender];
        require(amount > 0, "CrowdSourcer: Not contributed");
        
        contributors[msg.sender] = 0;
        (bool success, ) = msg.sender.call{value: amount}("");
        
        require(success, "CrowdSourcer: Withdraw failed");
    }
    receive() external payable {
        require(status == Status.Depositing, "CrowdSourcer: Not depositing");
        require(msg.value <= type(uint128).max, "CrowdSourcer: Overflow when casting to uint128");
        uint128 amount = uint128(msg.value);
        require(amount <= targetDecisionDeposit - totalContributed, "CrowdSourcer: Contributing more than neccesary");
        contributors[msg.sender] += amount;
        totalContributed += amount;
    }
}
```
## Lifecycle Overview
```
┌─────────────────────┐
│   EVM Smart         │   (Contract calls precompile)
│   Contract          │
│                     │
│ - submitInline()    │ → Submit small referendum
│ - submitLookup()    │ → Submit large referendum (via preimage)
│ - voteStandard()    │ → Cast conviction vote
│ - delegate()        │ → Delegate voting power
└──────────┬──────────┘
           │
           │ Calls Precompile
           ▼
┌─────────────────────┐
│   EVM Precompile    │   (Rust implementation)
│   (Address: 0x...)  │
│                     │
│ - Type conversion   │ → Solidity types ↔ Substrate types
│ - Origin mapping    │ → GovernanceOrigin → RuntimeOrigin
│ - Deposit handling  │ → msg.value → Balance
└──────────┬──────────┘
           │
           │ Dispatches to Pallets
           ▼
┌──────────────────────────────────────────────────┐
│                                                  │
│  ┌─────────────────┐  ┌─────────────────┐        │
│  │ pallet_referenda│  │pallet_conviction│        │
│  │                 │  │   _voting       │        │
│  │ - submit()      │  │ - vote()        │        │
│  │ - place_deposit │  │ - delegate()    │        │
│  │ - tally()       │  │ - remove_vote() │        │
│  │ (Lifecycle mgmt)│  │ (Vote tracking) │        │
│  └─────────────────┘  └─────────────────┘        │
│                                                  │
└──────────────────────────────────────────────────┘
```
## Appendix: Full Interface Design
For transparency and future planning, we've designed the complete interface. This is available for review but **not included in this proposal's scope**.
### Proposed Full Solidity Interfaces
We include here the rest of the functions not defined in the minimal interfaces. They should be added alongside the previously defined functions.
### ConvictionVoting
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
/// @title ConvictionVoting Precompile Interface
interface IConvictionVoting {
    
   // The functions defined before...
   
   /// @notice Remove someone else's expired vote
   /// @param target The account whose vote to remove
   /// @param class The class of the poll
   /// @param pollIndex The poll index
   function removeOtherVote(
       address target,
       uint16 class,
       uint256 pollIndex
   ) external;
    /// @notice Get the locked balance for an account in a trackId
    /// @param who The account to query
    /// @param trackId The governance track to query
    /// @return The locked amount
    function getLockedBalance(address who, uint16 trackId) external view returns (uint128);
    /// @notice Get the maximum locked balance across all trackIds.
    /// @param who The account to query
    /// @return The total locked amount (max of all locks along governance tracks)
    function getTotalLocked(address who) external view returns (uint128);
    }
```
### IReferenda
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
/// @title Referenda Precompile Interface
interface IReferenda {
    
    // The functions defined before...
    /// @notice Get metadata hash for a referendum.
    /// @param referendumIndex The index of the referendum to query.
    function getMetadata(uint32 referendumIndex) external view returns (bytes32);
    /// @notice Get deciding status for an ongoing referendum
    /// @param referendumIndex The referendum index
    /// @return exists Whether the referendum exists and is ongoing
    /// @return isDeciding Whether referendum is in deciding phase
    /// @return decidingSince Block number when deciding started
    /// @return confirming Block number when confirming ends (0 if not confirming)
    function getDecidingStatus(
        uint32 referendumIndex
    )
        external
        view
        returns (
            bool exists,
            bool isDeciding,
            uint32 decidingSince,
            uint32 confirming
        );
}
```
## References
- [Parity Polkadot SDK](https://github.com/paritytech/polkadot-sdk/)
- [frame_support docs](https://paritytech.github.io/polkadot-sdk/master/frame_support/index.html)
- [Polkadot Fellows Runtimes](https://github.com/polkadot-fellows/runtimes)
- [Polkassembly Governance UI](https://github.com/polkassembly/governance-ui/tree/main)
- [Subsquare](https://github.com/opensquare-network/subsquare)
- [frame-contrib](https://github.com/virto-network/frame-contrib/)
- [Kusama WFC#538](https://kusama.subsquare.io/referenda/538)
- [OppenZeppelin - Governance](https://docs.openzeppelin.com/contracts/5.x/api/governance)