Governance Precompile Usage
Address: 0x0000000000000000000000000000000000001006
The Sei governance precompile enables EVM applications to participate in Sei’s on-chain governance process. This allows users and smart contracts to submit proposals, vote on them, deposit tokens, and query governance information directly through the EVM interface.
How Does the Governance Precompile Work?
The governance precompile at address 0x0000000000000000000000000000000000001006
exposes functions like vote()
, voteWeighted()
, deposit()
, and submitProposal()
.
- Direct Integration: EVM contracts and dApps can interact with governance like any other smart contract method.
- Native Execution: Governance operations are executed at the chain level for maximum efficiency.
- Seamless: No need for separate wallet integrations.
Use Cases
- Voting Interfaces: Create user-friendly governance participation tools.
- Automated Governance: Smart contracts that vote based on predetermined strategies.
- Governance Aggregators: Build platforms that aggregate and simplify governance participation.
Sei Governance Overview
Sei governance operates on a structured timeline with specific parameters:
Governance Process
- Proposal Submission: Submit proposal with minimum 3,500 SEI deposit
- Deposit Period: 2 days to accumulate minimum deposit or reach expedited threshold
- Voting Period: 3 days (1 day for expedited) of active voting once minimum deposit is met
- Tallying: Results calculated based on quorum and voting thresholds
Critical Mainnet Considerations:
- High Stakes: Mainnet governance involves real SEI tokens and binding decisions
- Minimum Deposit: 3500 SEI minimum, 7000 SEI for expedited
- Deposit Risk: Deposits will be burned if your proposal receives >33.4% NoWithVeto votes
- Voting Power Required: Must stake SEI with validators to participate
- Time Limits: Strict 2-day deposit period and 3-day voting period
- Real Impact: Successful proposals can change network parameters
Functions
The governance precompile exposes the following functions:
Transaction Functions
/// Cast a vote on the specified proposal.
/// @param proposalID The ID of the proposal to vote on.
/// @param option The option to vote for. (1=Yes, 2=Abstain, 3=No, 4=NoWithVeto)
/// @return Whether the vote was successfully cast.
function vote(
uint64 proposalID,
int32 option
) external returns (bool success);
struct WeightedVoteOption {
int32 option; // Vote option (1=Yes, 2=Abstain, 3=No, 4=NoWithVeto)
string weight; // Weight as decimal string (e.g., "0.7") - MUST sum to exactly 1.0 across all options
}
/// Cast a weighted vote on a governance proposal (vote splitting)
/// @param proposalID The ID of the proposal to vote on
/// @param options Array of weighted vote options, weights MUST sum to exactly 1.0 or transaction will fail
/// @return success Whether the vote was successfully cast
function voteWeighted(
uint64 proposalID,
WeightedVoteOption[] calldata options
) external returns (bool success);
/// Deposit funds into the specified proposal.
/// @param proposalID The ID of the proposal to deposit to.
/// @return Whether the tokens were successfully deposited.
function deposit(
uint64 proposalID
) payable external returns (bool success);
/// Submit a new governance proposal. Deposit should be provided via msg.value
/// @param proposalJSON JSON string containing proposal details e.g.:
/// {
/// "title": "Proposal Title",
/// "description": "Proposal Description",
/// "type": "Text", // Optional, defaults to "Text" if empty
/// "is_expedited": false // Optional
/// }
/// @return proposalID The ID of the created proposal
function submitProposal(
string calldata proposalJSON
) payable external returns (uint64 proposalID);
Using the Contract
Setup
Prerequisites
Before getting started, ensure you have:
- Node.js (v16 or higher)
- npm or yarn package manager
- MetaMask or compatible EVM wallet configured for Sei Mainnet
- SEI tokens for gas fees and governance deposits (minimum 3,500 SEI for proposals)
Install Dependencies
Install the required packages for interacting with Sei precompiles:
# Install ethers.js for smart contract interactions
npm install ethers
# Install Sei EVM bindings for precompile addresses and ABIs
npm install @sei-js/precompiles@2.1.2
# Install dotenv for managing private keys (optional but recommended)
npm install dotenv
Import Precompile Components
// Import Governance precompile address and ABI
// View the entire ABI here: https://github.com/sei-protocol/sei-chain/tree/main/precompiles/gov
import { GOVERNANCE_PRECOMPILE_ABI, GOVERNANCE_PRECOMPILE_ADDRESS } from '@sei-js/precompiles';
import { ethers } from 'ethers';
0x0000000000000000000000000000000000001006
Get SEI Tokens
Purchase SEI tokens from supported exchanges:
- Centralized Exchanges: Binance, Coinbase, KuCoin, Gate.io
- Decentralized Exchanges: Astroport, Dragonswap (on Sei)
- Bridge Options: Various cross-chain bridges support SEI
Contract Initialization
Set up your provider, signer, and contract instance:
// Using MetaMask as the signer and provider (browser environment)
const provider = new ethers.BrowserProvider(window.ethereum);
await provider.send('eth_requestAccounts', []);
const signer = await provider.getSigner();
// Or for Node.js environment
const provider = new ethers.JsonRpcProvider('https://evm-rpc.sei-apis.com');
const signer = new ethers.Wallet('YOUR_PRIVATE_KEY', provider);
// Create a contract instance for the governance precompile
const governance = new ethers.Contract(GOVERNANCE_PRECOMPILE_ADDRESS, GOVERNANCE_PRECOMPILE_ABI, signer);
Vote Options
The governance system uses the following vote options:
Option | Value | Description | Impact |
---|---|---|---|
Yes | 1 | Vote in favor of the proposal | Proposal passes if threshold met |
Abstain | 2 | Abstain from voting | Counts toward quorum only |
No | 3 | Vote against the proposal | Opposes proposal |
NoWithVeto | 4 | Vote against with veto | Opposes Proposal & burns deposit if > 33.4% |
Voting Power
Your voting power is determined by:
- Staked SEI: Amount of SEI you have staked with validators
- Delegation: SEI delegated to validators counts toward voting power
- Liquid SEI: Unstaked SEI does not provide voting power
Validation Functions
Before making governance transactions, use these helper functions to validate inputs:
// Validate vote option
function isValidVoteOption(option: number): boolean {
return option >= 1 && option <= 4;
}
// Validate weighted vote options sum to 1.0 - CRITICAL FOR TRANSACTION SUCCESS
function validateWeightedOptions(options: Array<{ option: number; weight: string }>): boolean {
const totalWeight = options.reduce((sum, opt) => sum + parseFloat(opt.weight), 0);
// Check if total weight equals 1.0 (with small tolerance for floating point precision)
// Note: Sei governance is strict - even small deviations will cause transaction failure
const isValidTotal = Math.abs(totalWeight - 1.0) < 0.000001;
// Check if all options are valid
const allOptionsValid = options.every((opt) => isValidVoteOption(opt.option));
if (!isValidTotal) {
console.error(`Weights sum to ${totalWeight}, but MUST equal exactly 1.0`);
}
return isValidTotal && allOptionsValid;
}
// Validate proposal JSON structure
function validateProposalJSON(proposalJSON: string): boolean {
try {
const proposal = JSON.parse(proposalJSON);
return proposal.title && proposal.description && typeof proposal.title === 'string' && typeof proposal.description === 'string' && proposal.title.length > 0 && proposal.description.length > 0;
} catch {
return false;
}
}
// Check if deposit meets minimum requirement
function validateDepositAmount(depositAmount: bigint, isExpedited: boolean = false): boolean {
const MINIMUM_DEPOSIT = ethers.parseUnits('3500', 18); // 3,500 SEI minimum
const EXPEDITED_DEPOSIT = ethers.parseUnits('7000', 18); // 7,000 SEI for expedited
const requiredAmount = isExpedited ? EXPEDITED_DEPOSIT : MINIMUM_DEPOSIT;
return depositAmount >= requiredAmount;
}
// Check if user has sufficient voting power
async function checkVotingPower(userAddress: string): Promise<bigint> {
// This would typically query staking information
// Implementation depends on your specific setup
console.log(`Checking voting power for ${userAddress}`);
return ethers.parseUnits('0', 18); // Placeholder - implement actual check
}
Step-by-Step Guide: Using the Governance Precompile
Submit a Proposal
Important Deposit Requirements:
- Minimum deposit: 3,500 SEI required for proposal to enter voting period
- Expedited processing: 7,000 SEI if the voting period should only be 1 day long
- Deposit period: 2 days to meet minimum or proposal fails and deposits are burned
- Multiple deposits: Anyone can contribute to reach the minimum threshold
async function submitGovernanceProposal(title: string, description: string, depositAmount: bigint, isExpedited: boolean = false): Promise<bigint> {
// Validate inputs
if (!title || !description) {
throw new Error('Title and description are required');
}
if (title.length < 5 || title.length > 200) {
throw new Error('Title must be between 5 and 200 characters');
}
if (description.length < 20 || description.length > 10000) {
throw new Error('Description must be between 20 and 10,000 characters');
}
// Proposal details
const proposal = {
title: title.trim(),
description: description.trim(),
type: 'Text',
is_expedited: isExpedited
};
// Convert to JSON string and validate
const proposalJSON = JSON.stringify(proposal);
if (!validateProposalJSON(proposalJSON)) {
throw new Error('Invalid proposal JSON structure');
}
// Validate deposit amount
if (!validateDepositAmount(depositAmount, isExpedited)) {
const required = isExpedited ? '7,000 SEI' : '3,500 SEI';
const provided = ethers.formatEther(depositAmount);
throw new Error(`Insufficient deposit. Required: ${required}, Provided: ${provided} SEI`);
}
// Check balance before submitting
const balance = await provider.getBalance(await signer.getAddress());
const totalCost = depositAmount + ethers.parseUnits('0.1', 18); // Add gas buffer
if (balance < totalCost) {
throw new Error(`Insufficient balance. Need ${ethers.formatEther(totalCost)} SEI (including gas)`);
}
try {
console.log('Submitting governance proposal...');
console.log(`Title: ${title}`);
console.log(`Deposit: ${ethers.formatEther(depositAmount)} SEI`);
console.log(`Expedited: ${isExpedited}`);
const tx = await governance.submitProposal(proposalJSON, {
value: depositAmount,
gasLimit: 500000
});
console.log('Transaction submitted:', tx.hash);
console.log('Waiting for confirmation...');
const receipt = await tx.wait();
// Extract proposal ID from events
let proposalID: bigint | null = null;
for (const log of receipt.logs) {
try {
const parsed = governance.interface.parseLog(log);
if (parsed && parsed.name === 'ProposalSubmitted') {
proposalID = parsed.args.proposalID;
break;
}
} catch {
continue;
}
}
if (!proposalID) {
throw new Error('Could not extract proposal ID from transaction receipt');
}
console.log('✅ Proposal submitted successfully!');
console.log(`Proposal ID: ${proposalID.toString()}`);
console.log(`Transaction hash: ${receipt.hash}`);
console.log(`View proposal: https://sei.explorers.guru/proposal/${proposalID.toString()}`);
return proposalID;
} catch (error: any) {
console.error('Failed to submit proposal:', error);
if (error.message.includes('insufficient funds')) {
throw new Error('Insufficient funds for proposal deposit and gas fees');
} else if (error.message.includes('invalid deposit')) {
throw new Error('Deposit amount does not meet minimum requirements');
} else {
throw error;
}
}
}
// Usage example
const proposalTitle = 'Increase Block Size Limit';
const proposalDescription = `
This proposal suggests increasing the block size limit from the current value to improve transaction throughput on the Sei network.
## Rationale
Current network usage patterns show we are approaching capacity limits during peak times. Increasing the block size will:
1. Reduce transaction fees during high usage periods
2. Improve user experience with faster confirmation times
3. Support growing ecosystem adoption
## Implementation
The parameter change would be implemented through the existing governance mechanism with a 2-week transition period.
## Risks
- Slightly increased hardware requirements for validators
- Potential centralization concerns (mitigated by current validator distribution)
The benefits significantly outweigh the minimal risks involved.
`.trim();
const depositAmount = ethers.parseUnits('4000', 18); // 4,000 SEI deposit
await submitGovernanceProposal(proposalTitle, proposalDescription, depositAmount, false);
Vote on a Proposal
// Simple voting with comprehensive validation
async function voteOnProposal(proposalID: bigint, voteOption: number): Promise<void> {
// Validate vote option
if (!isValidVoteOption(voteOption)) {
throw new Error('Invalid vote option. Must be 1 (Yes), 2 (Abstain), 3 (No), or 4 (NoWithVeto)');
}
// Check voting power (implement based on your staking setup)
const userAddress = await signer.getAddress();
console.log(`Checking voting eligibility for ${userAddress}...`);
// Verify proposal exists and is in voting period
try {
// You would typically query proposal status here
console.log(`Voting on proposal ${proposalID.toString()}...`);
const voteOptionNames = {
1: 'Yes',
2: 'Abstain',
3: 'No',
4: 'NoWithVeto'
};
console.log(`Vote option: ${voteOptionNames[voteOption as keyof typeof voteOptionNames]}`);
const tx = await governance.vote(proposalID, voteOption, {
gasLimit: 200000
});
console.log('Transaction submitted:', tx.hash);
const receipt = await tx.wait();
console.log('✅ Vote cast successfully!');
console.log(`Proposal ID: ${proposalID.toString()}`);
console.log(`Vote: ${voteOptionNames[voteOption as keyof typeof voteOptionNames]}`);
console.log(`Transaction hash: ${receipt.hash}`);
console.log(`View on explorer: https://seitrace.com/tx/${receipt.hash}`);
} catch (error: any) {
console.error('Failed to cast vote:', error);
if (error.message.includes('proposal does not exist')) {
throw new Error('Proposal does not exist or voting period has ended');
} else if (error.message.includes('already voted')) {
throw new Error('You have already voted on this proposal');
} else if (error.message.includes('insufficient voting power')) {
throw new Error('No voting power - you must stake SEI to participate in governance');
} else {
throw error;
}
}
}
// Usage examples
const proposalID = 1n;
// Vote Yes
await voteOnProposal(proposalID, 1);
// Vote No with Veto (burns deposit if >33.4% of votes)
await voteOnProposal(proposalID, 4);
Weighted Voting
// Weighted voting with comprehensive validation
async function castWeightedVote(proposalID: bigint, weightedOptions: Array<{ option: number; weight: string }>): Promise<void> {
// Comprehensive validation
if (weightedOptions.length === 0) {
throw new Error('At least one vote option is required');
}
if (weightedOptions.length > 4) {
throw new Error('Cannot have more than 4 vote options');
}
// Validate each option and check for duplicates
const seenOptions = new Set<number>();
let totalWeight = 0;
for (const { option, weight } of weightedOptions) {
// Validate option
if (!isValidVoteOption(option)) {
throw new Error(`Invalid vote option: ${option}. Must be 1-4`);
}
// Check for duplicates
if (seenOptions.has(option)) {
throw new Error(`Duplicate vote option: ${option}`);
}
seenOptions.add(option);
// Validate weight
const weightNum = parseFloat(weight);
if (isNaN(weightNum) || weightNum <= 0 || weightNum > 1) {
throw new Error(`Invalid weight: ${weight}. Must be number between 0 and 1`);
}
totalWeight += weightNum;
}
// CRITICAL: Check total weight
if (Math.abs(totalWeight - 1.0) > 0.000001) {
throw new Error(`CRITICAL: Total weight must equal exactly 1.0, got ${totalWeight.toFixed(6)}. Transaction will fail.`);
}
try {
console.log('Casting weighted vote...');
console.log(`Proposal ID: ${proposalID.toString()}`);
console.log('Vote distribution:');
const voteOptionNames = { 1: 'Yes', 2: 'Abstain', 3: 'No', 4: 'NoWithVeto' };
let totalPercent = 0;
for (const { option, weight } of weightedOptions) {
const percentage = (parseFloat(weight) * 100).toFixed(1);
console.log(` ${percentage}% ${voteOptionNames[option as keyof typeof voteOptionNames]}`);
totalPercent += parseFloat(percentage);
}
console.log(`Total: ${totalPercent.toFixed(1)}%`);
const tx = await governance.voteWeighted(proposalID, weightedOptions, {
gasLimit: 300000 // Higher gas limit for weighted voting
});
console.log('Transaction submitted:', tx.hash);
const receipt = await tx.wait();
console.log('✅ Weighted vote cast successfully!');
console.log(`Transaction hash: ${receipt.hash}`);
console.log(`View on explorer: https://seitrace.com/tx/${receipt.hash}`);
} catch (error: any) {
console.error('Failed to cast weighted vote:', error);
if (error.message.includes('weights must sum to 1')) {
throw new Error('Vote weights must sum to exactly 1.0. Check your weight calculations.');
} else {
throw error;
}
}
}
// Usage examples
// Example 1: Split decision (60% Yes, 40% Abstain)
const splitVote = [
{ option: 1, weight: '0.6' }, // 60% Yes
{ option: 2, weight: '0.4' } // 40% Abstain
// Total: 0.6 + 0.4 = 1.0 ✅
];
await castWeightedVote(proposalID, splitVote);
// Example 2: Complex distribution
const complexVote = [
{ option: 1, weight: '0.45' }, // 45% Yes
{ option: 2, weight: '0.25' }, // 25% Abstain
{ option: 3, weight: '0.20' }, // 20% No
{ option: 4, weight: '0.10' } // 10% NoWithVeto
// Total: 0.45 + 0.25 + 0.20 + 0.10 = 1.0 ✅
];
await castWeightedVote(proposalID, complexVote);
// Example 3: This would FAIL - weights don't sum to 1.0
const badVote = [
{ option: 1, weight: '0.7' },
{ option: 2, weight: '0.2' }
// Total: 0.7 + 0.2 = 0.9 ❌ WILL FAIL
];
// await castWeightedVote(proposalID, badVote); // Don't run this!
Deposit to a Proposal
// Add deposit to help proposal reach minimum threshold
async function addDepositToProposal(proposalID: bigint, depositAmount: bigint): Promise<void> {
if (depositAmount <= 0) {
throw new Error('Deposit amount must be greater than 0');
}
// Check balance
const userAddress = await signer.getAddress();
const balance = await provider.getBalance(userAddress);
const totalCost = depositAmount + ethers.parseUnits('0.05', 18); // Add gas buffer
if (balance < totalCost) {
throw new Error(`Insufficient balance. Need ${ethers.formatEther(totalCost)} SEI (including gas), ` + `but have ${ethers.formatEther(balance)} SEI`);
}
try {
console.log('Adding deposit to proposal...');
console.log(`Proposal ID: ${proposalID.toString()}`);
console.log(`Deposit amount: ${ethers.formatEther(depositAmount)} SEI`);
const tx = await governance.deposit(proposalID, {
value: depositAmount,
gasLimit: 200000
});
console.log('Transaction submitted:', tx.hash);
const receipt = await tx.wait();
console.log('✅ Deposit added successfully!');
console.log(`Proposal ID: ${proposalID.toString()}`);
console.log(`Amount deposited: ${ethers.formatEther(depositAmount)} SEI`);
console.log(`Transaction hash: ${receipt.hash}`);
console.log(`View proposal: https://sei.explorers.guru/proposal/${proposalID.toString()}`);
} catch (error: any) {
console.error('Failed to add deposit:', error);
if (error.message.includes('proposal does not exist')) {
throw new Error('Proposal does not exist');
} else if (error.message.includes('deposit period ended')) {
throw new Error('Deposit period has ended for this proposal');
} else {
throw error;
}
}
}
// Usage example
const proposalID = 1n;
const additionalDeposit = ethers.parseUnits('1000', 18); // 1,000 SEI
await addDepositToProposal(proposalID, additionalDeposit);
Complete Integration Example
Create a comprehensive governance interaction script:
const { ethers } = require('ethers');
const { GOVERNANCE_PRECOMPILE_ABI, GOVERNANCE_PRECOMPILE_ADDRESS } = require('@sei-js/evm');
require('dotenv').config();
// Validation functions
function isValidVoteOption(option) {
return option >= 1 && option <= 4;
}
function validateWeightedOptions(options) {
const totalWeight = options.reduce((sum, opt) => sum + parseFloat(opt.weight), 0);
const isValidTotal = Math.abs(totalWeight - 1.0) < 0.000001;
const allOptionsValid = options.every((opt) => isValidVoteOption(opt.option));
return isValidTotal && allOptionsValid;
}
function validateProposalJSON(proposalJSON) {
try {
const proposal = JSON.parse(proposalJSON);
return proposal.title && proposal.description && typeof proposal.title === 'string' && typeof proposal.description === 'string' && proposal.title.length > 0 && proposal.description.length > 0;
} catch {
return false;
}
}
function validateDepositAmount(depositAmount, isExpedited = false) {
const MINIMUM_DEPOSIT = ethers.parseUnits('3500', 18); // 3,500 SEI
const EXPEDITED_DEPOSIT = ethers.parseUnits('7000', 18); // 7,000 SEI
const required = isExpedited ? EXPEDITED_DEPOSIT : MINIMUM_DEPOSIT;
return depositAmount >= required;
}
async function main() {
try {
// Connect to Sei Mainnet
console.log('🌐 Connecting to Sei Mainnet...');
const provider = new ethers.JsonRpcProvider('https://evm-rpc.sei-apis.com');
// Use your private key from environment variable
const privateKey = process.env.PRIVATE_KEY;
if (!privateKey) {
throw new Error('Please set PRIVATE_KEY in .env file');
}
const wallet = new ethers.Wallet(privateKey, provider);
const userAddress = wallet.address;
console.log(`👤 Wallet address: ${userAddress}`);
// Check balance
const balance = await provider.getBalance(userAddress);
const balanceEther = ethers.formatEther(balance);
console.log(`💰 Wallet balance: ${balanceEther} SEI\n`);
// Verify sufficient balance for mainnet governance
const minimumRequired = ethers.parseUnits('3600', 18); // 3,600 SEI (3,500 + buffer)
if (balance < minimumRequired) {
console.log('❌ Insufficient balance for mainnet governance participation');
console.log(` Required: At least 3,600 SEI (3,500 for proposal + 100 buffer)`);
console.log(` Current: ${balanceEther} SEI`);
console.log(' Please acquire more SEI tokens from exchanges or DEXs');
return;
}
// Initialize governance precompile contract
const governance = new ethers.Contract(GOVERNANCE_PRECOMPILE_ADDRESS, GOVERNANCE_PRECOMPILE_ABI, wallet);
console.log('🏛️ === Sei Mainnet Governance Demo ===\n');
// 1. Submit a governance proposal
console.log('1️⃣ Submitting a governance proposal...');
const proposal = {
title: 'Demo: EVM Governance Integration Test',
description: `This is a demonstration proposal submitted through the governance precompile to showcase seamless EVM-Cosmos governance integration.
## Purpose
This proposal demonstrates:
- EVM smart contract governance participation
- Cross-environment compatibility
- Native governance functionality through precompiles
## Technical Details
- Submitted via governance precompile at ${GOVERNANCE_PRECOMPILE_ADDRESS}
- Using ethers.js for transaction management
- Mainnet deployment with real SEI deposits
## Voting Recommendation
This is a test proposal. Vote according to your preference to test the system.`,
type: 'Text',
is_expedited: false
};
const proposalJSON = JSON.stringify(proposal);
// Validate proposal
if (!validateProposalJSON(proposalJSON)) {
throw new Error('Invalid proposal JSON structure');
}
// Use mainnet-appropriate deposit
const depositAmount = ethers.parseUnits('3500', 18); // Exactly minimum required
if (!validateDepositAmount(depositAmount)) {
throw new Error('Deposit amount insufficient for mainnet');
}
try {
console.log(` 📋 Title: ${proposal.title}`);
console.log(` 💵 Deposit: ${ethers.formatEther(depositAmount)} SEI`);
console.log(' 🚀 Submitting transaction...');
const submitTx = await governance.submitProposal(proposalJSON, {
value: depositAmount,
gasLimit: 500000,
gasPrice: ethers.parseUnits('100', 'gwei') // Set appropriate gas price
});
console.log(` 📝 Transaction hash: ${submitTx.hash}`);
console.log(' ⏳ Waiting for confirmation...');
const submitReceipt = await submitTx.wait();
// Extract proposal ID safely
let proposalID = null;
for (const log of submitReceipt.logs) {
try {
const parsed = governance.interface.parseLog(log);
if (parsed && parsed.name === 'ProposalSubmitted') {
proposalID = parsed.args.proposalID;
break;
}
} catch {
continue;
}
}
if (!proposalID) {
console.log(' ⚠️ Could not extract proposal ID. Check transaction manually.');
console.log(` 🔗 Transaction: https://seitrace.com/tx/${submitReceipt.hash}`);
return;
}
console.log(` ✅ Proposal submitted successfully!`);
console.log(` 🆔 Proposal ID: ${proposalID.toString()}`);
console.log(` 🔗 View proposal: https://sei.explorers.guru/proposal/${proposalID.toString()}`);
// 2. Demonstrate voting
console.log(`\n2️⃣ Voting on proposal ${proposalID.toString()}...`);
const voteOption = 1; // Yes
const voteNames = { 1: 'Yes', 2: 'Abstain', 3: 'No', 4: 'NoWithVeto' };
console.log(` 🗳️ Vote option: ${voteNames[voteOption]}`);
console.log(' 🚀 Submitting vote...');
const voteTx = await governance.vote(proposalID, voteOption, {
gasLimit: 200000,
gasPrice: ethers.parseUnits('100', 'gwei')
});
console.log(` 📝 Transaction hash: ${voteTx.hash}`);
await voteTx.wait();
console.log(' ✅ Vote cast successfully!');
console.log(` 🔗 Transaction: https://seitrace.com/tx/${voteTx.hash}`);
// 3. Demonstrate weighted voting
console.log(`\n3️⃣ Casting weighted vote on proposal ${proposalID.toString()}...`);
const weightedOptions = [
{ option: 1, weight: '0.7' }, // 70% Yes
{ option: 2, weight: '0.2' }, // 20% Abstain
{ option: 3, weight: '0.1' } // 10% No
// CRITICAL: Total = 0.7 + 0.2 + 0.1 = 1.0 ✅
];
if (!validateWeightedOptions(weightedOptions)) {
throw new Error('CRITICAL: Invalid weighted options - transaction would fail');
}
console.log(' 📊 Vote distribution:');
weightedOptions.forEach(({ option, weight }) => {
const percent = (parseFloat(weight) * 100).toFixed(1);
console.log(` ${percent}% ${voteNames[option]}`);
});
console.log(' 🚀 Submitting weighted vote...');
const weightedTx = await governance.voteWeighted(proposalID, weightedOptions, {
gasLimit: 300000,
gasPrice: ethers.parseUnits('100', 'gwei')
});
console.log(` 📝 Transaction hash: ${weightedTx.hash}`);
await weightedTx.wait();
console.log(' ✅ Weighted vote cast successfully!');
console.log(` 🔗 Transaction: https://seitrace.com/tx/${weightedTx.hash}`);
// 4. Add additional deposit
console.log(`\n4️⃣ Adding deposit to proposal ${proposalID.toString()}...`);
const additionalDeposit = ethers.parseUnits('500', 18); // 500 SEI
console.log(` 💵 Additional deposit: ${ethers.formatEther(additionalDeposit)} SEI`);
console.log(' 🚀 Submitting deposit...');
const depositTx = await governance.deposit(proposalID, {
value: additionalDeposit,
gasLimit: 200000,
gasPrice: ethers.parseUnits('100', 'gwei')
});
console.log(` 📝 Transaction hash: ${depositTx.hash}`);
await depositTx.wait();
console.log(' ✅ Additional deposit added successfully!');
console.log(` 🔗 Transaction: https://seitrace.com/tx/${depositTx.hash}`);
// Summary
console.log('\n🎉 === Governance Demo Completed Successfully! ===');
console.log(`📊 Proposal ID: ${proposalID.toString()}`);
console.log(`💰 Total deposited: ${ethers.formatEther(depositAmount.add(additionalDeposit))} SEI`);
console.log('🔗 View all governance proposals: https://sei.explorers.guru/proposals');
} catch (error) {
if (error.message.includes('insufficient funds')) {
console.log('\n❌ Insufficient funds for governance participation');
console.log(' Ensure you have enough SEI for deposits and gas fees');
} else if (error.message.includes('already voted')) {
console.log('\n⚠️ You have already voted on this proposal');
} else {
throw error;
}
}
} catch (error) {
console.error('\n❌ Error:', error.message);
console.log('\n🔧 Troubleshooting tips:');
console.log('1. Ensure PRIVATE_KEY is set in .env file');
console.log('2. Verify sufficient SEI balance (minimum 3,600 SEI)');
console.log('3. Check network connectivity to Sei mainnet');
console.log('4. Confirm wallet has staked SEI for voting power');
process.exit(1);
}
}
// Run the demo
main().catch(console.error);
Running the Example
- Create a new directory and initialize npm:
mkdir sei-governance-mainnet
cd sei-governance-mainnet
npm init -y
- Install dependencies:
npm install ethers @sei-js/precompiles@^2.1.2 dotenv
- Create a
.env
file:
PRIVATE_KEY=your_private_key_here
-
Create the demo file: Copy the complete integration example above into
governance-mainnet-demo.js
-
Ensure you have sufficient SEI:
- Minimum 3,500 SEI for proposal deposit
- Additional SEI for gas fees and optional additional deposits
- Staked SEI for voting power
-
Run the script:
node governance-mainnet-demo.js
Expected Output
🌐 Connecting to Sei Mainnet...
👤 Wallet address: 0x742d35Cc6634C0532925a3b844Bc6789e065f3B
💰 Wallet balance: 5000.5 SEI
🏛️ === Sei Mainnet Governance Demo ===
1️⃣ Submitting a governance proposal...
📋 Title: Demo: EVM Governance Integration Test
💵 Deposit: 3500.0 SEI
🚀 Submitting transaction...
📝 Transaction hash: 0x123...abc
⏳ Waiting for confirmation...
✅ Proposal submitted successfully!
🆔 Proposal ID: 42
🔗 View proposal: https://sei.explorers.guru/proposal/42
2️⃣ Voting on proposal 42...
🗳️ Vote option: Yes
🚀 Submitting vote...
📝 Transaction hash: 0x456...def
✅ Vote cast successfully!
🔗 Transaction: https://seitrace.com/tx/0x456...def
3️⃣ Casting weighted vote on proposal 42...
📊 Vote distribution:
70.0% Yes
20.0% Abstain
10.0% No
🚀 Submitting weighted vote...
📝 Transaction hash: 0x789...ghi
✅ Weighted vote cast successfully!
🔗 Transaction: https://seitrace.com/tx/0x789...ghi
4️⃣ Adding deposit to proposal 42...
💵 Additional deposit: 500.0 SEI
🚀 Submitting deposit...
📝 Transaction hash: 0xabc...123
✅ Additional deposit added successfully!
🔗 Transaction: https://seitrace.com/tx/0xabc...123
🎉 === Governance Demo Completed Successfully! ===
📊 Proposal ID: 42
💰 Total deposited: 4000.0 SEI
🔗 View all governance proposals: https://sei.explorers.guru/proposals
Troubleshooting
Common Issues and Solutions
Insufficient Deposit
// Check minimum deposit requirement based on proposal type
const MINIMUM_DEPOSIT = ethers.parseUnits('3500', 18); // 3,500 SEI minimum
const EXPEDITED_DEPOSIT = ethers.parseUnits('7000', 18); // 7,000 SEI for expedited
function checkDepositRequirement(depositAmount: bigint, isExpedited: boolean = false): void {
const requiredAmount = isExpedited ? EXPEDITED_DEPOSIT : MINIMUM_DEPOSIT;
const networkName = 'mainnet';
if (depositAmount < requiredAmount) {
const required = ethers.formatEther(requiredAmount);
const provided = ethers.formatEther(depositAmount);
throw new Error(`Deposit too low for ${networkName}${isExpedited ? ' (expedited)' : ''}. Required: ${required} SEI, Provided: ${provided} SEI`);
}
}
No Voting Power
// Check if user has staked SEI for voting power
async function checkVotingEligibility(userAddress: string): Promise<void> {
console.log('Checking voting eligibility...');
// Note: You would typically integrate with staking queries here
// This is a placeholder for the actual implementation
console.log('⚠️ Reminder: You must have staked SEI to participate in governance voting');
console.log(' Liquid SEI does not provide voting power');
console.log(' Delegate your SEI to validators to gain voting power');
console.log(' Visit: https://app.sei.io/stake to stake your tokens');
}
Invalid Vote Option
// Validate vote option with descriptive error
function validateVoteOptionWithDescription(option: number): void {
const voteOptions = {
1: 'Yes - Vote in favor of the proposal',
2: 'Abstain - Neutral vote that counts toward quorum',
3: 'No - Vote against the proposal',
4: 'NoWithVeto - Strong opposition that can burn deposits'
};
if (!(option in voteOptions)) {
const validOptions = Object.entries(voteOptions)
.map(([key, value]) => `${key}: ${value}`)
.join('\n');
throw new Error(`Invalid vote option: ${option}\n\nValid options:\n${validOptions}`);
}
}
Weighted Vote Validation
// Comprehensive weighted vote validation with helpful errors
function validateWeightedVoteComprehensive(options: Array<{ option: number; weight: string }>): void {
if (options.length === 0) {
throw new Error('At least one vote option is required');
}
if (options.length > 4) {
throw new Error('Cannot have more than 4 vote options');
}
let totalWeight = 0;
const seenOptions = new Set<number>();
const voteOptionNames = { 1: 'Yes', 2: 'Abstain', 3: 'No', 4: 'NoWithVeto' };
for (let i = 0; i < options.length; i++) {
const { option, weight } = options[i];
// Validate option
if (option < 1 || option > 4 || !Number.isInteger(option)) {
throw new Error(
`Invalid vote option at index ${i}: ${option}. Must be integer 1-4\n` +
`Valid options: ${Object.entries(voteOptionNames)
.map(([k, v]) => `${k}=${v}`)
.join(', ')}`
);
}
// Check for duplicate options
if (seenOptions.has(option)) {
throw new Error(`Duplicate vote option: ${option} (${voteOptionNames[option as keyof typeof voteOptionNames]})\n` + `Each vote option can only be used once`);
}
seenOptions.add(option);
// Validate weight
const weightNum = parseFloat(weight);
if (isNaN(weightNum)) {
throw new Error(`Invalid weight at index ${i}: "${weight}" is not a valid number`);
}
if (weightNum <= 0) {
throw new Error(`Invalid weight at index ${i}: ${weight}. Weight must be greater than 0`);
}
if (weightNum > 1) {
throw new Error(`Invalid weight at index ${i}: ${weight}. Weight cannot exceed 1.0`);
}
totalWeight += weightNum;
}
// Check total weight - CRITICAL for transaction success
if (Math.abs(totalWeight - 1.0) > 0.000001) {
const weightSummary = options.map(({ option, weight }) => `${voteOptionNames[option as keyof typeof voteOptionNames]}: ${weight}`).join(', ');
throw new Error(`CRITICAL: Total weight must equal exactly 1.0, got ${totalWeight.toFixed(6)}\n` + `Current weights: ${weightSummary}\n` + `Sum: ${totalWeight.toFixed(6)}\n` + `Transaction will fail if weights don't sum to 1.0`);
}
}
Error Code Reference
Error | Cause | Solution |
---|---|---|
proposal does not exist | Invalid proposal ID or proposal expired | Verify proposal ID and ensure voting period is still active |
inactive proposal | Voting or deposit period has ended | Check proposal status and timeline |
insufficient deposit | Deposit below 3,500 SEI | Increase deposit to the minimum required amount |
invalid vote option | Vote option not 1-4 | Use a valid option (1-Yes, 2-Abstain, 3-No, 4-NoWithVeto) |
already voted | Address has already voted on this proposal | Each address can vote only once per proposal |
weights must sum to 1 | Weighted votes don’t sum to 1.0 | CRITICAL — adjust weights so they total exactly 1.0 |
insufficient voting power | No staked SEI | Delegate SEI to validators to gain voting power |
deposit period ended | Deposits no longer accepted | Deposit period closes 2 days after submission |
Need Help? If you encounter issues:
- Discord: Sei Community Discord
- GitHub: Sei Protocol Repository
- Explorer: View Proposals on Sei Explorer