Skip to content

Commit

Permalink
Enable execution of proposal transactions by any account #30
Browse files Browse the repository at this point in the history
  • Loading branch information
Semen Medvedev committed Apr 29, 2022
1 parent 0e0f611 commit 3987c91
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 42 deletions.
10 changes: 5 additions & 5 deletions addin-vesting/program/src/voter_weight.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,26 @@ use spl_governance_tools::account::{
get_account_data,
};

use spl_governance_addin_api::voter_weight::VoterWeightRecord;
pub use spl_governance_addin_api::voter_weight::VoterWeightRecord;

/// ExtendedVoterWeightRecord account
/// The account is used as an api interface to provide voting power to the governance program
/// and to save information about total amount of deposited token
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub struct ExtendedVoterWeightRecord {
base: VoterWeightRecord,
pub base: VoterWeightRecord,

/// ExtendedVoterWeightRecord discriminator sha256("account:ExtendedVoterWeightRecord")[..8]
/// Note: The discriminator size must match the addin implementing program discriminator size
/// to ensure it's stored in the private space of the account data and it's unique
account_discriminator: [u8; 8],
pub account_discriminator: [u8; 8],

/// Total number of tokens owned by the account
total_amount: u64,
pub total_amount: u64,

/// Percentage of the total number of tokens for calculating the voting weight
/// (in hundredths of a percent)
vote_percentage: u16,
pub vote_percentage: u16,
}

impl ExtendedVoterWeightRecord {
Expand Down
80 changes: 74 additions & 6 deletions governance-lib/src/addin_vesting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,29 @@ use {
client::Client,
realm::Realm,
},
borsh::{BorshSerialize},
solana_sdk::{
pubkey::Pubkey,
signer::Signer,
instruction::Instruction,
program_error::ProgramError,
},
spl_governance_addin_vesting::{
state::VestingSchedule,
state::{
VestingAccountType,
VestingSchedule,
VestingRecord,
},
instruction::{deposit, deposit_with_realm},
voter_weight::get_voter_weight_record_address,
voter_weight::{
VoterWeightRecord,
ExtendedVoterWeightRecord,
get_voter_weight_record_address,
},
max_voter_weight::{
MaxVoterWeightRecord,
get_max_voter_weight_record_address,
},
},
};

Expand All @@ -39,6 +52,54 @@ impl<'a> AddinVesting<'a> {
vesting_account
}

pub fn get_vesting_account_size(&self, number_of_schedules: u32, realm: bool) -> usize {
let record = VestingRecord {
account_type: VestingAccountType::VestingRecord,
owner: Pubkey::default(),
mint: Pubkey::default(),
token: Pubkey::default(),
realm: if realm {Some(Pubkey::default())} else {None},
schedule: Vec::new(),
};
let schedule = VestingSchedule {
release_time: 0,
amount: 0,
};
record.try_to_vec().unwrap().len() + number_of_schedules as usize * schedule.try_to_vec().unwrap().len()
}

pub fn get_voter_weight_account_size(&self) -> usize {
let record_data = ExtendedVoterWeightRecord {
base: VoterWeightRecord {
account_discriminator: VoterWeightRecord::ACCOUNT_DISCRIMINATOR,
realm: Pubkey::default(),
governing_token_mint: Pubkey::default(),
governing_token_owner: Pubkey::default(),
voter_weight: 0,
voter_weight_expiry: None,
weight_action: None,
weight_action_target: None,
reserved: [0u8; 8],
},
account_discriminator: ExtendedVoterWeightRecord::ACCOUNT_DISCRIMINATOR,
total_amount: 0,
vote_percentage: 0,
};
record_data.try_to_vec().unwrap().len()
}

pub fn get_max_voter_weight_account_size(&self) -> usize {
let record_data = MaxVoterWeightRecord {
account_discriminator: MaxVoterWeightRecord::ACCOUNT_DISCRIMINATOR,
realm: Pubkey::default(),
governing_token_mint: Pubkey::default(),
max_voter_weight: 0,
max_voter_weight_expiry: None,
reserved: [0u8; 8],
};
record_data.try_to_vec().unwrap().len()
}

pub fn get_voter_weight_record_address(&self, owner: &Pubkey, realm: &Realm) -> Pubkey {
get_voter_weight_record_address(
&self.program_id,
Expand All @@ -47,8 +108,15 @@ impl<'a> AddinVesting<'a> {
owner)
}

pub fn get_max_voter_weight_record_address(&self, realm: &Realm) -> Pubkey {
get_max_voter_weight_record_address(
&self.program_id,
&realm.realm_address,
&realm.community_mint)
}

pub fn deposit_instruction(&self, source_token_authority: &Pubkey, source_token_account: &Pubkey,
vesting_owner: &Pubkey, vesting_token_account: &Pubkey, schedules: Vec<VestingSchedule>) -> Result<Instruction, ProgramError>
vesting_owner: &Pubkey, vesting_token_account: &Pubkey, schedules: Vec<VestingSchedule>, payer: Option<Pubkey>) -> Result<Instruction, ProgramError>
{
deposit(
&self.program_id,
Expand All @@ -57,13 +125,13 @@ impl<'a> AddinVesting<'a> {
&source_token_authority,
source_token_account,
vesting_owner,
&self.client.payer.pubkey(),
&payer.unwrap_or(self.client.payer.pubkey()),
schedules,
)
}

pub fn deposit_with_realm_instruction(&self, source_token_authority: &Pubkey, source_token_account: &Pubkey,
vesting_owner: &Pubkey, vesting_token_account: &Pubkey, schedules: Vec<VestingSchedule>, realm: &Realm) -> Result<Instruction, ProgramError>
vesting_owner: &Pubkey, vesting_token_account: &Pubkey, schedules: Vec<VestingSchedule>, realm: &Realm, payer: Option<Pubkey>) -> Result<Instruction, ProgramError>
{
deposit_with_realm(
&self.program_id,
Expand All @@ -72,7 +140,7 @@ impl<'a> AddinVesting<'a> {
&source_token_authority,
source_token_account,
vesting_owner,
&self.client.payer.pubkey(),
&payer.unwrap_or(self.client.payer.pubkey()),
schedules,
&realm.program_id,
&realm.realm_address,
Expand Down
17 changes: 9 additions & 8 deletions governance-lib/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use {
crate::errors::GovernanceLibError,
borsh::BorshDeserialize,
solana_sdk::{
account::Account,
account_utils::StateMut,
borsh::try_from_slice_unchecked,
commitment_config::CommitmentConfig,
Expand Down Expand Up @@ -125,15 +126,18 @@ impl<'a> Client<'a> {
RpcSendTransactionConfig {skip_preflight: true, ..RpcSendTransactionConfig::default()}).map_err(|e| e.into())
}

pub fn get_account(&self, account_key: &Pubkey) -> ClientResult<Option<Account>> {
let account_info = self.solana_client.get_account_with_commitment(
&account_key, self.solana_client.commitment())?.value;
Ok(account_info)
}

pub fn get_account_data_pack<T: Pack + IsInitialized>(
&self,
owner_program_id: &Pubkey,
account_key: &Pubkey,
) -> ClientResult<Option<T>> {
let account_info = &self.solana_client.get_account_with_commitment(
&account_key, self.solana_client.commitment())?.value;

if let Some(account_info) = account_info {
if let Some(account_info) = self.get_account(account_key)? {
if account_info.data.is_empty() {
return Err(GovernanceLibError::StateError(*account_key, "Account is empty".to_string()));
}
Expand All @@ -157,10 +161,7 @@ impl<'a> Client<'a> {
owner_program_id: &Pubkey,
account_key: &Pubkey,
) -> ClientResult<Option<T>> {
let account_info = &self.solana_client.get_account_with_commitment(
&account_key, self.solana_client.commitment())?.value;

if let Some(account_info) = account_info {
if let Some(account_info) = self.get_account(account_key)? {
if account_info.data.is_empty() {
return Err(GovernanceLibError::StateError(*account_key, "Account is empty".to_string()));
}
Expand Down
5 changes: 3 additions & 2 deletions governance-lib/src/realm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,13 +186,13 @@ impl<'a> Realm<'a> {
self.client.get_account_data_borsh::<RealmConfigAccount>(&self.program_id, &realm_config_address)
}

pub fn set_realm_config_instruction(&self, realm_authority: &Pubkey, realm_config: &RealmConfig) -> Instruction {
pub fn set_realm_config_instruction(&self, realm_authority: &Pubkey, realm_config: &RealmConfig, payer: Option<Pubkey>) -> Instruction {
set_realm_config(
&self.program_id,
&self.realm_address,
realm_authority,
realm_config.council_token_mint,
&self.client.payer.pubkey(),
&payer.unwrap_or(self.client.payer.pubkey()),
realm_config.community_voter_weight_addin,
realm_config.max_community_voter_weight_addin,
realm_config.min_community_weight_to_create_governance,
Expand All @@ -206,6 +206,7 @@ impl<'a> Realm<'a> {
self.set_realm_config_instruction(
&realm_authority.pubkey(),
realm_config,
None, // default payer
),
],
&[realm_authority],
Expand Down
106 changes: 85 additions & 21 deletions launch-script/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ mod helpers;
mod msig;

use crate::{
tokens::{get_mint_data, get_account_data, create_mint_instructions},
tokens::{
get_mint_data,
get_account_data,
create_mint_instructions,
assert_is_valid_account_data,
},
errors::{StateError, ScriptError},
wallet::Wallet,
helpers::{
Expand Down Expand Up @@ -247,6 +252,43 @@ fn process_environment(wallet: &Wallet, client: &Client, setup: bool, verbose: b
let max_voter_weight_record_address = fixed_weight_addin.setup_max_voter_weight_record(&realm).unwrap();
realm.settings_mut().max_voter_weight_record_address = Some(max_voter_weight_record_address);

// ------------ Transfer tokens to Vesting-addin MaxVoterWeightRecord ------
{
let record_address = vesting_addin.get_max_voter_weight_record_address(&realm);
let record_length = vesting_addin.get_max_voter_weight_account_size();
let record_lamports = Rent::default().minimum_balance(record_length);
executor.check_and_create_object("Vesting max_voter_weight_record",
client.get_account(&record_address)?,
|v| {
if v.lamports < record_lamports {
let transaction = client.create_transaction_with_payer_only(
&[
system_instruction::transfer(
&wallet.payer_keypair.pubkey(),
&record_address,
record_lamports - v.lamports,
),
],
)?;
return Ok(Some(transaction))
}
Ok(None)
},
|| {
let transaction = client.create_transaction_with_payer_only(
&[
system_instruction::transfer(
&wallet.payer_keypair.pubkey(),
&record_address,
record_lamports,
),
],
)?;
Ok(Some(transaction))
}
)?;
}

// -------------------- Create accounts for token_owner --------------------
let voter_list = fixed_weight_addin.get_voter_list()?;
for (i, voter_weight) in voter_list.iter().enumerate() {
Expand Down Expand Up @@ -280,6 +322,16 @@ fn process_environment(wallet: &Wallet, client: &Client, setup: bool, verbose: b
&wallet.community_pubkey,
&vesting_addin.find_vesting_account(&vesting_token_account),
).unwrap(),
system_instruction::transfer( // Charge VestingRecord
&wallet.payer_keypair.pubkey(),
&vesting_addin.find_vesting_account(&vesting_token_account),
Rent::default().minimum_balance(vesting_addin.get_vesting_account_size(1, true)),
),
system_instruction::transfer(
&wallet.payer_keypair.pubkey(), // Charge VoterWeightRecord
&vesting_addin.get_voter_weight_record_address(&voter_weight.voter, &realm),
Rent::default().minimum_balance(vesting_addin.get_voter_weight_account_size()),
),
],
&[&wallet.creator_keypair]
)?;
Expand All @@ -302,12 +354,8 @@ fn process_environment(wallet: &Wallet, client: &Client, setup: bool, verbose: b

executor.check_and_create_object(&seed, get_account_data(client, &token_account_address)?,
|d| {
if d.mint != wallet.community_pubkey {
return Err(StateError::InvalidTokenAccountMint(token_account_address, d.mint).into());
}
if d.owner != token_account_owner {
return Err(StateError::InvalidTokenAccountOwner(token_account_address, d.owner).into());
}
assert_is_valid_account_data(d, &token_account_address,
&wallet.community_pubkey, &token_account_owner)?;
Ok(None)
},
|| {
Expand Down Expand Up @@ -396,6 +444,33 @@ fn process_environment(wallet: &Wallet, client: &Client, setup: bool, verbose: b
}
)?;

// --------- Create NEON associated token account -------------
let governance_token_account = spl_associated_token_account::get_associated_token_address_with_program_id(
&governance.governance_address, &wallet.community_pubkey, &spl_token::id());
println!("Governance address: {}", governance.governance_address);
println!("Governance token account: {}", governance_token_account);

executor.check_and_create_object("NEON-token governance account",
get_account_data(client, &governance_token_account)?,
|d| {
assert_is_valid_account_data(d, &governance_token_account,
&wallet.community_pubkey, &governance.governance_address)?;
Ok(None)
},
|| {
let transaction = client.create_transaction_with_payer_only(
&[
spl_associated_token_account::create_associated_token_account(
&wallet.payer_keypair.pubkey(),
&governance.governance_address,
&wallet.community_pubkey,
).into(),
],
)?;
Ok(Some(transaction))
}
)?;

// --------------- Pass token and programs to governance ------
let mut collector = TransactionCollector::new(client, setup, verbose, "Pass under governance");
// 1. Mint
Expand Down Expand Up @@ -542,19 +617,6 @@ fn setup_proposal_tge(wallet: &Wallet, client: &Client, proposal_index: Option<u
println!("Governance address: {}", governance.governance_address);
println!("Governance token account: {}", governance_token_account);

if !client.account_exists(&governance_token_account) {
transaction_inserter.insert_transaction_checked(
"Create associated NEON-token for governance",
vec![
spl_associated_token_account::create_associated_token_account(
&wallet.payer_keypair.pubkey(),
&governance.governance_address,
&wallet.community_pubkey,
).into(),
],
)?;
}

let voter_list = fixed_weight_addin.get_voter_list()?;
let total_amount = voter_list.iter().map(|v| v.weight).sum::<u64>() +
EXTRA_TOKEN_ACCOUNTS.iter().map(|v| v.amount).sum::<u64>();
Expand Down Expand Up @@ -589,6 +651,7 @@ fn setup_proposal_tge(wallet: &Wallet, client: &Client, proposal_index: Option<u
&vesting_token_account, // vesting_token_account
schedule, // schedule
&realm, // realm
Some(governance.governance_address), // payer
)?.into(),
],
)?;
Expand Down Expand Up @@ -623,7 +686,8 @@ fn setup_proposal_tge(wallet: &Wallet, client: &Client, proposal_index: Option<u
max_community_voter_weight_addin: None,
min_community_weight_to_create_governance: 1, // TODO Verify parameters!
community_mint_max_vote_weight_source: MintMaxVoteWeightSource::FULL_SUPPLY_FRACTION,
}
},
Some(governance.governance_address), // payer
).into(),
],
)?;
Expand Down
1 change: 1 addition & 0 deletions launch-script/src/msig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ pub fn setup_msig(wallet: &Wallet, client: &Client, executor: &TransactionExecut
&vesting_token_account, // vesting_token_account
schedule, // schedule
&msig_realm, // realm
None, // default payer
)?.into(),
],
&[&wallet.creator_keypair],
Expand Down
Loading

0 comments on commit 3987c91

Please sign in to comment.