Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,9 @@ ByaDoc1TAiSkp3gGkRDR4bVSqbe55W37mjwYbTy38Tq3.json
HkAGc7akgEH7HxkHWNk3htZHMid7fmG2eU6SxyGf9PKa.json

.env
pm-testnet.json
pm-testnet.json

node_modules
.next

ax.json
2 changes: 1 addition & 1 deletion contract/Anchor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ govcontract = "AXnkQnEEMBsKcJ1gSXP1aW6tZMGWodzEaoB6b3bRib2r"
govcontract = "GoVpHPV3EY89hwKJjfw19jTdgMsGKG4UFSE2SfJqTuhc"

[programs.testnet]
govcontract = "GoVpHPV3EY89hwKJjfw19jTdgMsGKG4UFSE2SfJqTuhc"
govcontract = "6MX2RaV2vfTGv6c7zCmRAod2E6MdAgR6be2Vb3NsMxPW"

[registry]
url = "https://api.apr.dev"
Expand Down
2 changes: 1 addition & 1 deletion contract/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion contract/programs/govcontract/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ idl-build = ["anchor-lang/idl-build"]

[dependencies]
anchor-lang = { version = "0.31.1", features = ["init-if-needed"]}
gov-v1 = { git = "https://github.com/exo-tech-xyz/gov-v1", branch = "signer-check",features = ["cpi"] }
gov-v1 = { git = "https://github.com/dhruvsol/gov-v1-testnet", branch = "signer-check",features = ["cpi"] }

4 changes: 2 additions & 2 deletions contract/programs/govcontract/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ pub const BASIS_POINTS_MAX: u64 = 10_000;
// Anchor discriminator size
pub const ANCHOR_DISCRIMINATOR: usize = 8;

pub const MIN_PROPOSAL_STAKE_LAMPORTS: u64 = 100_000 * 1_000_000_000;
pub const MIN_PROPOSAL_STAKE_LAMPORTS: u64 = 1000 * 1_000_000_000;

pub const CLUSTER_SUPPORT_MULTIPLIER: u128 = 100;
pub const CLUSTER_SUPPORT_MULTIPLIER: u128 = 1000;

pub const CLUSTER_STAKE_MULTIPLIER: u128 = 5;

Expand Down
13 changes: 13 additions & 0 deletions contract/programs/govcontract/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,16 @@ pub struct MerkleRootFlushed {
pub new_snapshot_slot: u64,
pub flush_timestamp: i64,
}

#[event]
pub struct ProposalTimingAdjusted {
pub proposal_id: Pubkey,
pub author: Pubkey,
pub new_creation_timestamp: i64,
pub new_creation_epoch: u64,
pub new_start_epoch: u64,
pub new_end_epoch: u64,
pub new_snapshot_slot: u64,
pub new_consensus_result: Option<Pubkey>,
pub adjustment_timestamp: i64,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use anchor_lang::prelude::*;

use crate::{error::GovernanceError, events::ProposalTimingAdjusted, state::Proposal};

#[derive(Accounts)]
pub struct AdjustProposalTiming<'info> {
#[account(mut)]
pub signer: Signer<'info>, // Proposal author
#[account(
mut,
constraint = proposal.author == signer.key() @ GovernanceError::Unauthorized,
constraint = !proposal.finalized @ GovernanceError::ProposalFinalized,
)]
pub proposal: Account<'info, Proposal>,
}

impl<'info> AdjustProposalTiming<'info> {
pub fn adjust_timing(
&mut self,
creation_timestamp: Option<i64>,
creation_epoch: Option<u64>,
start_epoch: Option<u64>,
end_epoch: Option<u64>,
snapshot_slot: Option<u64>,
consensus_result: Option<Option<Pubkey>>,
) -> Result<()> {
let clock = Clock::get()?;

// Update fields if provided
if let Some(ts) = creation_timestamp {
self.proposal.creation_timestamp = ts;
}
if let Some(epoch) = creation_epoch {
self.proposal.creation_epoch = epoch;
}
if let Some(epoch) = start_epoch {
self.proposal.start_epoch = epoch;
}
if let Some(epoch) = end_epoch {
self.proposal.end_epoch = epoch;
}
if let Some(slot) = snapshot_slot {
self.proposal.snapshot_slot = slot;
}
if let Some(consensus_result_value) = consensus_result {
self.proposal.consensus_result = consensus_result_value;
}
emit!(ProposalTimingAdjusted {
proposal_id: self.proposal.key(),
author: self.signer.key(),
new_creation_timestamp: self.proposal.creation_timestamp,
new_creation_epoch: self.proposal.creation_epoch,
new_start_epoch: self.proposal.start_epoch,
new_end_epoch: self.proposal.end_epoch,
new_snapshot_slot: self.proposal.snapshot_slot,
new_consensus_result: self.proposal.consensus_result,
adjustment_timestamp: clock.unix_timestamp,
});

Ok(())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,14 @@ impl<'info> FlushMerkleRoot<'info> {
let clock = Clock::get()?;

// Clear the consensus_result
self.proposal.consensus_result = None;
require!(
self.proposal.snapshot_slot > 0,
GovernanceError::InvalidSnapshotSlot
);
require!(
self.proposal.consensus_result.is_some(),
GovernanceError::ConsensusResultNotSet
);

// Recalculate snapshot_slot based on current epoch
// Using the same logic as in support_proposal
Expand Down Expand Up @@ -66,6 +73,7 @@ impl<'info> FlushMerkleRoot<'info> {
b"proposal".as_ref(),
&proposal_seed_val,
vote_account_key.as_ref(),
&[self.proposal.proposal_bump],
];
let signer = &[&seeds[..]];
// Initialize the ballot box via CPI
Expand Down
2 changes: 2 additions & 0 deletions contract/programs/govcontract/src/instructions/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod adjust_proposal_timing;
pub mod cast_vote;
pub mod cast_vote_override;
pub mod create_proposal;
Expand All @@ -8,6 +9,7 @@ pub mod modify_vote;
pub mod modify_vote_override;
pub mod support_proposal;

pub use adjust_proposal_timing::*;
pub use cast_vote::*;
pub use cast_vote_override::*;
pub use create_proposal::*;
Expand Down
36 changes: 29 additions & 7 deletions contract/programs/govcontract/src/instructions/support_proposal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ use anchor_lang::{
prelude::*,
solana_program::{
epoch_stake::{get_epoch_stake_for_vote_account, get_epoch_total_stake},
instruction::Instruction,
program::invoke_signed,
vote::{program as vote_program, state::VoteState},
},
ToAccountMetas,
};
use borsh::BorshSerialize;

use crate::{
constants::*,
Expand Down Expand Up @@ -36,6 +40,7 @@ pub struct SupportProposal<'info> {
pub spl_vote_account: UncheckedAccount<'info>,

/// CHECK: Ballot box account - may or may not exist, checked with data_is_empty()
#[account(mut)]
pub ballot_box: UncheckedAccount<'info>,

/// CHECK: Ballot program account
Expand All @@ -44,9 +49,25 @@ pub struct SupportProposal<'info> {
)]
pub ballot_program: UncheckedAccount<'info>,

/// CHECK: Program config account
#[account(
seeds = [b"ProgramConfig"],
bump,
seeds::program = ballot_program.key(),
constraint = program_config.owner == &gov_v1::ID @ ProgramError::InvalidAccountOwner,
)]
pub program_config: UncheckedAccount<'info>,

pub system_program: Program<'info, System>,
}

pub struct InitBallotBox<'info> {
pub payer: AccountInfo<'info>,
pub proposal: AccountInfo<'info>,
pub ballot_box: AccountInfo<'info>,
pub program_config: AccountInfo<'info>,
pub system_program: AccountInfo<'info>,
}
impl<'info> SupportProposal<'info> {
pub fn support_proposal(&mut self, bumps: &SupportProposalBumps) -> Result<()> {
let clock = Clock::get()?;
Expand Down Expand Up @@ -103,30 +124,31 @@ impl<'info> SupportProposal<'info> {
// Create seed components with sufficient lifetime
let proposal_seed_val = self.proposal.proposal_seed.to_le_bytes();
let vote_account_key = self.proposal.vote_account_pubkey.key();

let seeds: &[&[u8]] = &[
b"proposal".as_ref(),
&proposal_seed_val,
vote_account_key.as_ref(),
&[self.proposal.proposal_bump],
];
let signer = &[&seeds[..]];
// Initialize the consensus result
let signer_seeds = &[&seeds[..]];

let cpi_ctx = CpiContext::new_with_signer(
self.ballot_program.to_account_info(),
gov_v1::cpi::accounts::InitBallotBox {
payer: self.signer.to_account_info(),
proposal: self.proposal.to_account_info(),
ballot_box: self.ballot_box.to_account_info(),
program_config: self.ballot_program.to_account_info(),
program_config: self.program_config.to_account_info(),
system_program: self.system_program.to_account_info(),
},
signer,
signer_seeds,
);

gov_v1::cpi::init_ballot_box(
cpi_ctx,
snapshot_slot,
self.proposal.proposal_seed, // we are not storing this
self.spl_vote_account.key(),
self.proposal.proposal_seed,
self.proposal.vote_account_pubkey,
)?;
}

Expand Down
22 changes: 21 additions & 1 deletion contract/programs/govcontract/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use instructions::*;

use gov_v1::StakeMerkleLeaf;

declare_id!("GoVpHPV3EY89hwKJjfw19jTdgMsGKG4UFSE2SfJqTuhc");
declare_id!("6MX2RaV2vfTGv6c7zCmRAod2E6MdAgR6be2Vb3NsMxPW");

#[program]
pub mod govcontract {
Expand Down Expand Up @@ -108,4 +108,24 @@ pub mod govcontract {
ctx.accounts.flush_merkle_root()?;
Ok(())
}

pub fn adjust_proposal_timing(
ctx: Context<AdjustProposalTiming>,
creation_timestamp: Option<i64>,
creation_epoch: Option<u64>,
start_epoch: Option<u64>,
end_epoch: Option<u64>,
snapshot_slot: Option<u64>,
consensus_result: Option<Option<Pubkey>>,
) -> Result<()> {
ctx.accounts.adjust_timing(
creation_timestamp,
creation_epoch,
start_epoch,
end_epoch,
snapshot_slot,
consensus_result,
)?;
Ok(())
}
}
12 changes: 12 additions & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.next
node_modules
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
.nextra
.nextra-cache
.nextra-cache-dev
.nextra-cache-prod
.nextra-cache-test
Binary file added docs/bun.lockb
Binary file not shown.
11 changes: 11 additions & 0 deletions docs/next.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import nextra from 'nextra';

// Set up Nextra with its configuration
const withNextra = nextra({
search: { codeblocks: false },
});

// Export the final Next.js config with Nextra included
export default withNextra({
// ... Add regular Next.js options here
});
21 changes: 21 additions & 0 deletions docs/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "svmgov-docs",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next --turbopack",
"build": "next build",
"start": "next start",
"postbuild": "pagefind --site .next/server/app --output-path public/_pagefind"
},
"dependencies": {
"next": "^16.0.2",
"nextra": "^4.6.0",
"nextra-theme-docs": "^4.6.0",
"react": "^19.2.0",
"react-dom": "^19.2.0"
},
"devDependencies": {
"pagefind": "^1.4.0"
}
}
27 changes: 27 additions & 0 deletions docs/src/app/[[...mdxPath]]/page.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { generateStaticParamsFor, importPage } from 'nextra/pages';
import { useMDXComponents as getMDXComponents } from '../../mdx-components';

export const generateStaticParams = generateStaticParamsFor('mdxPath');

export async function generateMetadata(props) {
const params = await props.params;
const { metadata } = await importPage(params.mdxPath);
return metadata;
}

const Wrapper = getMDXComponents().wrapper;

export default async function Page(props) {
const params = await props.params;
const {
default: MDXContent,
toc,
metadata,
sourceCode,
} = await importPage(params.mdxPath);
return (
<Wrapper toc={toc} metadata={metadata} sourceCode={sourceCode}>
<MDXContent {...props} params={params} />
</Wrapper>
);
}
Loading