Governance Precompile Usage
Submit proposals, vote, and query governance from EVM contracts using the governance precompile and ethers.js.
Address: 0x0000000000000000000000000000000000001006
The Paxeer governance precompile enables EVM applications to participate in Paxeer'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.
Paxeer Governance Overview
Paxeer governance operates on a structured timeline with specific parameters:
Governance Process
- Proposal Submission: Submit proposal with minimum 3,500 HPX 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 HPX tokens and binding decisions
- Minimum Deposit: 3500 HPX minimum, 7000 HPX for expedited
- Deposit Risk: Deposits will be burned if your proposal receives >33.4% NoWithVeto votes
- Voting Power Required: Must stake HPX 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 (v18 or higher)
- npm or yarn package manager
- MetaMask or compatible EVM wallet configured for Paxeer Mainnet
- HPX tokens for gas and governance deposits (minimum 3,500 HPX for proposals)
Install Dependencies
Install the required packages for interacting with Paxeer precompiles:
# Install ethers.js for smart contract interactions
npm install ethers
# Install Paxeer 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/paxeer-network/paxeer-chain/tree/main/precompiles/gov
import { GOVERNANCE_PRECOMPILE_ABI, GOVERNANCE_PRECOMPILE_ADDRESS } from '@sei-js/precompiles';
import { ethers } from 'ethers';
0x0000000000000000000000000000000000001006Get HPX Tokens
Purchase HPX tokens from supported exchanges:
- Centralized Exchanges: Binance, Coinbase, KuCoin, Gate.io
- Decentralized Exchanges: Astroport, Dragonswap (on Paxeer)
- Bridge Options: Various cross-chain bridges support HPX
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 HPX: Amount of HPX you have staked with validators
- Delegation: HPX delegated to validators counts toward voting power
- Liquid HPX: Unstaked HPX 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: Paxeer 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 HPX minimum
const EXPEDITED_DEPOSIT = ethers.parseUnits('7000', 18); // 7,000 HPX 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 HPX required for proposal to enter voting period
- Expedited processing: 7,000 HPX 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 HPX' : '3,500 HPX';
const provided = ethers.formatEther(depositAmount);
throw new Error(`Insufficient deposit. Required: ${required}, Provided: ${provided} HPX`);
}
// 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)} HPX (including gas)`);
}
try {
console.log('Submitting governance proposal...');
console.log(`Title: ${title}`);
console.log(`Deposit: ${ethers.formatEther(depositAmount)} HPX`);
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://paxeer.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 Paxeer 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 HPX 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://paxscan.io/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 HPX 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://paxscan.io/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)} HPX (including gas), ` + `but have ${ethers.formatEther(balance)} HPX`);
}
try {
console.log('Adding deposit to proposal...');
console.log(`Proposal ID: ${proposalID.toString()}`);
console.log(`Deposit amount: ${ethers.formatEther(depositAmount)} HPX`);
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)} HPX`);
console.log(`Transaction hash: ${receipt.hash}`);
console.log(`View proposal: https://paxeer.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 HPX
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/precompiles');
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 HPX
const EXPEDITED_DEPOSIT = ethers.parseUnits('7000', 18); // 7,000 HPX
const required = isExpedited ? EXPEDITED_DEPOSIT : MINIMUM_DEPOSIT;
return depositAmount >= required;
}
async function main() {
try {
// Connect to Paxeer Mainnet
console.log('🌐 Connecting to Paxeer 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} HPX\n`);
// Verify sufficient balance for mainnet governance
const minimumRequired = ethers.parseUnits('3600', 18); // 3,600 HPX (3,500 + buffer)
if (balance < minimumRequired) {
console.log('❌ Insufficient balance for mainnet governance participation');
console.log(` Required: At least 3,600 HPX (3,500 for proposal + 100 buffer)`);
console.log(` Current: ${balanceEther} HPX`);
console.log(' Please acquire more HPX tokens from exchanges or DEXs');
return;
}
// Initialize governance precompile contract
const governance = new ethers.Contract(GOVERNANCE_PRECOMPILE_ADDRESS, GOVERNANCE_PRECOMPILE_ABI, wallet);
console.log('🏛️ === Paxeer 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 HPX 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)} HPX`);
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://paxscan.io/tx/${submitReceipt.hash}`);
return;
}
console.log(` ✅ Proposal submitted successfully!`);
console.log(` 🆔 Proposal ID: ${proposalID.toString()}`);
console.log(` 🔗 View proposal: https://paxeer.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://paxscan.io/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://paxscan.io/tx/${weightedTx.hash}`);
// 4. Add additional deposit
console.log(`\n4️⃣ Adding deposit to proposal ${proposalID.toString()}...`);
const additionalDeposit = ethers.parseUnits('500', 18); // 500 HPX
console.log(` 💵 Additional deposit: ${ethers.formatEther(additionalDeposit)} HPX`);
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://paxscan.io/tx/${depositTx.hash}`);
// Summary
console.log('\n🎉 === Governance Demo Completed Successfully! ===');
console.log(`📊 Proposal ID: ${proposalID.toString()}`);
console.log(`💰 Total deposited: ${ethers.formatEther(depositAmount + additionalDeposit)} HPX`);
console.log('🔗 View all governance proposals: https://paxeer.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 HPX 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 HPX balance (minimum 3,600 HPX)');
console.log('3. Check network connectivity to Paxeer mainnet');
console.log('4. Confirm wallet has staked HPX for voting power');
process.exit(1);
}
}
// Run the demo
main().catch(console.error);Running the Example
- Create a new directory and initialize npm:
mkdir paxeer-governance-mainnet
cd paxeer-governance-mainnet
npm init -y
- Install dependencies:
npm install ethers @sei-js/precompiles@^2.1.2 dotenv
- Create a
.envfile:
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 HPX:
- Minimum 3,500 HPX for proposal deposit
- Additional HPX for gas and optional additional deposits
- Staked HPX for voting power
-
Run the script:
node governance-mainnet-demo.js
Expected Output
🌐 Connecting to Paxeer Mainnet...
👤 Wallet address: 0x742d35Cc6634C0532925a3b844Bc6789e065f3B
💰 Wallet balance: 5000.5 HPX
🏛️ === Paxeer Mainnet Governance Demo ===
1️⃣ Submitting a governance proposal...
📋 Title: Demo: EVM Governance Integration Test
💵 Deposit: 3500.0 HPX
🚀 Submitting transaction...
📝 Transaction hash: 0x123...abc
⏳ Waiting for confirmation...
✅ Proposal submitted successfully!
🆔 Proposal ID: 42
🔗 View proposal: https://paxeer.explorers.guru/proposal/42
2️⃣ Voting on proposal 42...
🗳️ Vote option: Yes
🚀 Submitting vote...
📝 Transaction hash: 0x456...def
✅ Vote cast successfully!
🔗 Transaction: https://paxscan.io/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://paxscan.io/tx/0x789...ghi
4️⃣ Adding deposit to proposal 42...
💵 Additional deposit: 500.0 HPX
🚀 Submitting deposit...
📝 Transaction hash: 0xabc...123
✅ Additional deposit added successfully!
🔗 Transaction: https://paxscan.io/tx/0xabc...123
🎉 === Governance Demo Completed Successfully! ===
📊 Proposal ID: 42
💰 Total deposited: 4000.0 HPX
🔗 View all governance proposals: https://paxeer.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 HPX minimum
const EXPEDITED_DEPOSIT = ethers.parseUnits('7000', 18); // 7,000 HPX 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} HPX, Provided: ${provided} HPX`);
}
}
No Voting Power
// Check if user has staked HPX 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 HPX to participate in governance voting');
console.log(' Liquid HPX does not provide voting power');
console.log(' Delegate your HPX to validators to gain voting power');
console.log(' Visit: https://docs.paxeer.app/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 HPX | 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 HPX | Delegate HPX 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: Paxeer Community Discord
- GitHub: Paxeer Protocol Repository
- Explorer: View Proposals on Paxeer Explorer