Skip to content

Commit

Permalink
Simplify the writing of object creation #30
Browse files Browse the repository at this point in the history
  • Loading branch information
Semen Medvedev committed Apr 29, 2022
1 parent 392f362 commit 506c49e
Show file tree
Hide file tree
Showing 7 changed files with 846 additions and 325 deletions.
35 changes: 35 additions & 0 deletions governance-lib/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,41 @@ impl<'a> Client<'a> {
}
}

pub fn send_transaction(&self, transaction: &Transaction) -> ClientResult<Signature> {
//self.solana_client.send_and_confirm_transaction(&transaction)
self.solana_client.send_and_confirm_transaction_with_spinner_and_config(&transaction,
self.solana_client.commitment(),
RpcSendTransactionConfig {skip_preflight: true, ..RpcSendTransactionConfig::default()}).map_err(|e| e.into())
}

pub fn create_transaction_with_payer_only(
&self,
instructions: &[Instruction],
) -> ClientResult<Transaction> {
self.create_transaction::<[&dyn solana_sdk::signature::Signer;0]>(
instructions,
&[],
)
}

pub fn create_transaction<T: Signers>(
&self,
instructions: &[Instruction],
signing_keypairs: &T,
) -> ClientResult<Transaction> {
let mut transaction: Transaction =
Transaction::new_with_payer(
instructions,
Some(&self.payer.pubkey()),
);

let blockhash = self.solana_client.get_latest_blockhash().unwrap();
transaction.partial_sign(&[self.payer], blockhash);
transaction.sign(signing_keypairs, blockhash);

Ok(transaction)
}

pub fn send_and_confirm_transaction_with_payer_only(
&self,
instructions: &[Instruction],
Expand Down
26 changes: 17 additions & 9 deletions governance-lib/src/realm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,21 +83,29 @@ impl<'a> Realm<'a> {
self.client.get_account_data_borsh::<RealmV2>(&self.program_id, &self.realm_address)
}

pub fn create_realm_instruction(&self, realm_authority: &Pubkey, voter_weight_addin: Option<Pubkey>, max_voter_weight_addin: Option<Pubkey>) -> Instruction {
create_realm(
&self.program_id,
&realm_authority,
&self.community_mint,
&self.client.payer.pubkey(),
None,
voter_weight_addin,
max_voter_weight_addin,
self.realm_name.clone(),
MIN_COMMUNITY_WEIGHT_TO_CREATE_GOVERNANCE,
MintMaxVoteWeightSource::FULL_SUPPLY_FRACTION,
)
}

pub fn create_realm(&self, realm_authority: &'a Keypair, voter_weight_addin: Option<Pubkey>, max_voter_weight_addin: Option<Pubkey>) -> ClientResult<Signature> {
self.client.send_and_confirm_transaction_with_payer_only(
&[
create_realm(
&self.program_id,
self.create_realm_instruction(
&realm_authority.pubkey(),
&self.community_mint,
&self.client.payer.pubkey(),
None,
voter_weight_addin,
max_voter_weight_addin,
self.realm_name.clone(),
MIN_COMMUNITY_WEIGHT_TO_CREATE_GOVERNANCE,
MintMaxVoteWeightSource::FULL_SUPPLY_FRACTION,
)
),
],
)
}
Expand Down
20 changes: 12 additions & 8 deletions governance-lib/src/token_owner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,17 +70,21 @@ impl<'a> TokenOwner<'a> {
)
}

pub fn set_delegate_instruction(&self, authority: &Pubkey, new_delegate: &Option<Pubkey>) -> Instruction {
set_governance_delegate(
&self.realm.program_id,
&authority,
&self.realm.realm_address,
&self.realm.community_mint,
&self.token_owner_address,
new_delegate,
)
}

pub fn set_delegate(&self, authority: &Keypair, new_delegate: &Option<Pubkey>) -> ClientResult<Signature> {
self.realm.client.send_and_confirm_transaction(
&[
set_governance_delegate(
&self.realm.program_id,
&authority.pubkey(),
&self.realm.realm_address,
&self.realm.community_mint,
&self.token_owner_address,
new_delegate,
),
self.set_delegate_instruction(&authority.pubkey(), new_delegate),
],
&[authority],
)
Expand Down
249 changes: 249 additions & 0 deletions governance-test-scripts/src/helpers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
use crate::{
errors::{StateError, ScriptError},
};
use colored::*;
use solana_sdk::{
signer::{
Signer,
keypair::Keypair,
},
instruction::Instruction,
transaction::Transaction,
signature::Signature,
};

use spl_governance::{
state::{
proposal_transaction::InstructionData,
},
};

use governance_lib::{
client::Client,
proposal::Proposal,
token_owner::TokenOwner,
};


macro_rules! println_item {
($format:literal, $($item:expr),*) => {
println!(concat!("\x1b[34m", $format, "\x1b[0m"), $($item),*);
}
}

macro_rules! println_error {
($format:literal, $($item:expr),*) => {
println!(concat!("\x1b[31m", $format, "\x1b[0m"), $($item),*);
}
}

pub struct TransactionExecutor<'a> {
pub client: &'a Client<'a>,
pub setup: bool,
pub verbose: bool,
}

impl<'a> TransactionExecutor<'a> {
pub fn check_and_create_object<T,V,C>(&self, name: &str,
object: Option<T>, verify: V, create: C) -> Result<Option<Signature>,ScriptError>
where
V: FnOnce(&T) -> Result<Option<Transaction>,ScriptError>,
C: FnOnce() -> Result<Option<Transaction>,ScriptError>,
T: std::fmt::Debug,
{
if let Some(data) = object {
if self.verbose {println_item!("{}: {:?}", name, data);};
match verify(&data) {
Ok(None) => {
println!("{}: correct", name);
},
Ok(Some(transaction)) => {
if self.setup {
let result = self.client.send_transaction(&transaction);
match result {
Ok(signature) => {
println!("{}: updated in trx {}", name, signature);
return Ok(Some(signature));
},
Err(error) => {
println_error!("{}: failed update with {}", name, error);
return Err(error.into());
}
};
} else {
if self.verbose {println_item!("{}: {:?}", name, transaction);};
println!("{}: will be updated", name);
}
},
Err(error) => {
println_error!("{}: wrong object {:?}", name, error);
if self.setup {return Err(error);}
}
}
} else {
match create() {
Ok(None) => {
println!("{}: missed ok", name);
},
Ok(Some(transaction)) => {
if self.setup {
let result = self.client.send_transaction(&transaction);
match result {
Ok(signature) => {
println!("{}: created in trx {}", name, signature);
return Ok(Some(signature));
},
Err(error) => {
println_error!("{}: failed create with {}", name, error);
return Err(error.into());
}
};
} else {
if self.verbose {println_item!("{}: {:?}", name, transaction);};
println!("{}: will be created", name);
}
},
Err(error) => {
println_error!("{}: can't be created: {:?}", name, error);
if self.setup {return Err(error);}
}
}
}
Ok(None)
}
}

pub struct TransactionCollector<'a> {
pub client: &'a Client<'a>,
pub setup: bool,
pub verbose: bool,
pub name: String,
instructions: Vec<Instruction>,
signers: Vec<&'a dyn Signer>,
}

impl<'a> TransactionCollector<'a> {
pub fn new(client: &'a Client<'a>, setup: bool, verbose: bool, name: &str) -> Self {
println!("{}: collect instructions...", name);
Self {
client,
setup,
verbose,
name: name.to_string(),
instructions: Vec::new(),
signers: Vec::new(),
}
}

fn add_signers(&mut self, keypairs: Vec<&'a Keypair>) {
for keypair in keypairs {
self.signers.push(keypair as &dyn Signer);
}
}

pub fn check_and_create_object<T,V,C>(&mut self, name: &str,
object: Option<T>, verify: V, create: C) -> Result<(),ScriptError>
where
V: FnOnce(&T) -> Result<Option<(Vec<Instruction>,Vec<&'a Keypair>)>,ScriptError>,
C: FnOnce() -> Result<Option<(Vec<Instruction>,Vec<&'a Keypair>)>,ScriptError>,
T: std::fmt::Debug,
{
if let Some(data) = object {
if self.verbose {println_item!("{}: {:?}", name, data);};
match verify(&data) {
Ok(None) => {
println!("{}: correct", name);
},
Ok(Some((instructions,signers,))) => {
if self.verbose {println_item!("{}: {:?}", name, instructions);};
println!("{}: update instructions was added", name);
self.instructions.extend(instructions);
self.add_signers(signers);
},
Err(error) => {
println_error!("{}: wrong object {:?}", name, error);
if self.setup {return Err(error);}
}
}
} else {
match create() {
Ok(None) => {
println!("{}: missed ok", name);
},
Ok(Some((instructions,signers,))) => {
if self.verbose {println_item!("{}: {:?}", name, instructions);};
println!("{}: create instructions was added", name);
self.instructions.extend(instructions);
self.add_signers(signers);
},
Err(error) => {
println_error!("{}: can't be created: {:?}", name, error);
if self.setup {return Err(error);}
}
}
}
Ok(())
}

pub fn execute_transaction(&self) -> Result<Option<Signature>,ScriptError> {
if self.setup {
let result = if self.signers.is_empty() {
self.client.send_and_confirm_transaction_with_payer_only(&self.instructions)
} else {
self.client.send_and_confirm_transaction(&self.instructions, &self.signers)
};
match result {
Ok(signature) => {
println!("{}: processed in trx {}", self.name, signature);
return Ok(Some(signature));
},
Err(error) => {
println_error!("{}: failed process with {}", self.name, error);
return Err(error.into());
}
}
} else {
println!("{}: no instructions for execute", self.name);
Ok(None)
}
}
}

pub struct ProposalTransactionInserter<'a> {
pub proposal: &'a Proposal<'a>,
pub creator_keypair: &'a Keypair,
pub creator_token_owner: &'a TokenOwner<'a>,
pub hold_up_time: u32,
pub setup: bool,
pub verbose: bool,

pub proposal_transaction_index: u16,
}

impl<'a> ProposalTransactionInserter<'a> {
pub fn insert_transaction_checked(&mut self, name: &str, instructions: Vec<InstructionData>) -> Result<(), ScriptError> {
if let Some(transaction_data) = self.proposal.get_proposal_transaction_data(0, self.proposal_transaction_index)? {
if self.verbose {println_item!("Proposal transaction '{}'/{}: {:?}", name, self.proposal_transaction_index, transaction_data);};
if transaction_data.instructions != instructions {
let error = StateError::InvalidProposalTransaction(self.proposal_transaction_index);
if self.setup {return Err(error.into())} else {println_error!("Proposal transaction '{}'/{}: {:?}", name, self.proposal_transaction_index, error);}
} else {
println!("Proposal transaction '{}'/{} correct", name, self.proposal_transaction_index);
}
} else if self.setup {
let signature = self.proposal.insert_transaction(
&self.creator_keypair,
&self.creator_token_owner,
0, self.proposal_transaction_index, self.hold_up_time,
instructions
)?;
println!("Proposal transaction '{}'/{} was inserted in trx: {}", name, self.proposal_transaction_index, signature);
} else {
println!("Proposal transaction '{}'/{} will be inserted", name, self.proposal_transaction_index);
}
self.proposal_transaction_index += 1;
Ok(())
}
}


Loading

0 comments on commit 506c49e

Please sign in to comment.