From f823b2d787c9981f29ae2c6f559e3fa52707e6ea Mon Sep 17 00:00:00 2001 From: Semen Medvedev Date: Sat, 16 Apr 2022 00:47:09 +0700 Subject: [PATCH] Implemented proposal for Token Genesis Event #30 --- Cargo.toml | 1 + addin-vesting/program/src/instruction.rs | 2 +- artifacts/creator.keypair | 1 + governance-lib/Cargo.toml | 1 + governance-lib/src/addin_vesting.rs | 155 +++++++++ governance-lib/src/client.rs | 86 ++++- governance-lib/src/governance.rs | 19 +- governance-lib/src/lib.rs | 1 + governance-lib/src/proposal.rs | 166 ++++++++- governance-lib/src/realm.rs | 169 ++++++++- governance-lib/src/token_owner.rs | 52 ++- launch-script/Cargo.toml | 21 ++ launch-script/README.md | 25 ++ launch-script/community_mint.keypair | 1 + launch-script/governance.keypair | 1 + launch-script/src/main.rs | 416 +++++++++++++++++++++++ launch-script/src/token_distribution.rs | 20 ++ launch-script/src/tokens.rs | 218 ++++++++++++ 18 files changed, 1335 insertions(+), 20 deletions(-) create mode 100644 artifacts/creator.keypair create mode 100644 governance-lib/src/addin_vesting.rs create mode 100644 launch-script/Cargo.toml create mode 100644 launch-script/README.md create mode 100644 launch-script/community_mint.keypair create mode 100644 launch-script/governance.keypair create mode 100644 launch-script/src/main.rs create mode 100644 launch-script/src/token_distribution.rs create mode 100644 launch-script/src/tokens.rs diff --git a/Cargo.toml b/Cargo.toml index 4400872..d6a5eeb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "addin-vesting/cli", "governance-test-scripts", "governance-lib", + "launch-script", ] exclude = [ "solana-program-library", diff --git a/addin-vesting/program/src/instruction.rs b/addin-vesting/program/src/instruction.rs index 0608ed1..8cf043b 100644 --- a/addin-vesting/program/src/instruction.rs +++ b/addin-vesting/program/src/instruction.rs @@ -176,7 +176,7 @@ pub fn deposit_with_realm( AccountMeta::new_readonly(*source_token_owner, true), AccountMeta::new(*source_token_account, false), AccountMeta::new_readonly(*vesting_owner, false), - AccountMeta::new_readonly(*payer, true), + AccountMeta::new(*payer, true), AccountMeta::new_readonly(*governance_id, false), AccountMeta::new_readonly(*realm, false), diff --git a/artifacts/creator.keypair b/artifacts/creator.keypair new file mode 100644 index 0000000..a0db4ff --- /dev/null +++ b/artifacts/creator.keypair @@ -0,0 +1 @@ +[167,119,139,142,62,185,218,144,63,242,132,68,181,77,117,88,16,84,69,103,210,49,114,237,151,235,178,55,159,74,223,248,70,191,83,171,68,147,169,18,114,190,120,151,80,58,132,43,50,211,123,76,233,134,230,10,26,102,162,8,173,8,2,54] \ No newline at end of file diff --git a/governance-lib/Cargo.toml b/governance-lib/Cargo.toml index f8728c7..e9b0df4 100644 --- a/governance-lib/Cargo.toml +++ b/governance-lib/Cargo.toml @@ -16,3 +16,4 @@ spl-governance-addin-api= { version = "0.1.1", path = "../solana-program-library spl-governance-addin-mock = { version = "0.1", path = "../solana-program-library/governance/addin-mock/program" } spl-governance-addin-fixed-weights = { version = "0.1", path = "../addin-fixed-weights/program" } +spl-governance-addin-vesting = { path = "../addin-vesting/program" } diff --git a/governance-lib/src/addin_vesting.rs b/governance-lib/src/addin_vesting.rs new file mode 100644 index 0000000..dd6f703 --- /dev/null +++ b/governance-lib/src/addin_vesting.rs @@ -0,0 +1,155 @@ +use { + crate::{ + client::SplGovernanceInteractor, + realm::Realm, + }, + solana_sdk::{ + pubkey::Pubkey, + signer::{Signer, keypair::Keypair}, + instruction::Instruction, + transaction::Transaction, + program_error::ProgramError, + }, + spl_governance_addin_vesting::{ + state::VestingSchedule, + instruction::{deposit, deposit_with_realm}, + voter_weight::get_voter_weight_record_address, + }, + solana_client::{ + client_error::ClientError, + }, +}; + +#[derive(Debug)] +pub struct AddinVesting<'a> { + interactor: &'a SplGovernanceInteractor<'a>, + pub program_id: Pubkey, +} + + +impl<'a> AddinVesting<'a> { + pub fn new(interactor: &'a SplGovernanceInteractor, program_id: Pubkey) -> Self { + AddinVesting { + interactor, + program_id, + } + } + + pub fn find_vesting_account(&self, vesting_token_account: &Pubkey) -> Pubkey { + let (vesting_account,_) = Pubkey::find_program_address( + &[&vesting_token_account.as_ref()], + &self.program_id, + ); + vesting_account + } + + pub fn get_voter_weight_record_address(&self, owner: &Pubkey, realm: &Realm) -> Pubkey { + get_voter_weight_record_address( + &self.program_id, + &realm.address, + &realm.community_mint, + owner) + } + + pub fn deposit(&self, source_token_authority: &Pubkey, source_token_account: &Pubkey, + vesting_owner: &Pubkey, vesting_token_account: &Pubkey, schedules: Vec) -> Result + { + deposit( + &self.program_id, + &spl_token::id(), + vesting_token_account, + &source_token_authority, + source_token_account, + vesting_owner, + &self.interactor.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, realm: &Realm) -> Result + { + deposit_with_realm( + &self.program_id, + &spl_token::id(), + vesting_token_account, + &source_token_authority, + source_token_account, + vesting_owner, + &self.interactor.payer.pubkey(), + schedules, + &realm.interactor.spl_governance_program_address, + &realm.address, + &realm.community_mint, + ) + } + +/* pub fn setup_max_voter_weight_record(&self, realm: &Realm) -> Result { + use spl_governance_addin_fixed_weights::instruction; + let (max_voter_weight_record_pubkey,_): (Pubkey,u8) = instruction::get_max_voter_weight_address( + &self.program_id, + &realm.address, + &realm.community_mint, + ); + + if !self.interactor.account_exists(&max_voter_weight_record_pubkey) { + let setup_max_voter_weight_record_instruction: Instruction = + instruction::setup_max_voter_weight_record( + &self.program_id, + &realm.address, + &realm.community_mint, + &self.interactor.payer.pubkey(), + ); + + let transaction: Transaction = + Transaction::new_signed_with_payer( + &[ + setup_max_voter_weight_record_instruction, + ], + Some(&self.interactor.payer.pubkey()), + &[ + self.interactor.payer, + ], + self.interactor.solana_client.get_latest_blockhash().unwrap(), + ); + + self.interactor.solana_client.send_and_confirm_transaction(&transaction) + .map_err(|_|())?; + } + Ok(max_voter_weight_record_pubkey) + } + + pub fn setup_voter_weight_record(&self, realm: &Realm, token_owner: &Pubkey) -> Result { + let (voter_weight_record_pubkey,_): (Pubkey,u8) = spl_governance_addin_fixed_weights::instruction::get_voter_weight_address( + &self.program_id, + &realm.address, + &realm.community_mint, + token_owner); + + if !self.interactor.account_exists(&voter_weight_record_pubkey) { + let setup_voter_weight_record_instruction: Instruction = + spl_governance_addin_fixed_weights::instruction::setup_voter_weight_record( + &self.program_id, + &realm.address, + &realm.data.community_mint, + token_owner, + &self.interactor.payer.pubkey(), + ); + + let transaction: Transaction = + Transaction::new_signed_with_payer( + &[ + setup_voter_weight_record_instruction, + ], + Some(&self.interactor.payer.pubkey()), + &[ + self.interactor.payer, + ], + self.interactor.solana_client.get_latest_blockhash().unwrap(), + ); + + self.interactor.solana_client.send_and_confirm_transaction(&transaction).unwrap(); + } + Ok(voter_weight_record_pubkey) + }*/ +} diff --git a/governance-lib/src/client.rs b/governance-lib/src/client.rs index 79f94e1..16fd44b 100644 --- a/governance-lib/src/client.rs +++ b/governance-lib/src/client.rs @@ -1,14 +1,18 @@ use { - crate::realm::Realm, + crate::realm::{Realm, RealmSettings}, borsh::BorshDeserialize, solana_sdk::{ + borsh::try_from_slice_unchecked, commitment_config::CommitmentConfig, pubkey::Pubkey, instruction::Instruction, transaction::Transaction, signer::{Signer, keypair::Keypair}, + signers::Signers, + signature::Signature, + program_pack::{Pack, IsInitialized}, }, - std::fmt, + std::{cell::RefCell, fmt}, spl_governance::{ state::{ enums::MintMaxVoteWeightSource, @@ -53,6 +57,79 @@ impl<'a> SplGovernanceInteractor<'a> { spl_governance_voter_weight_addin_address: addin_address, } } + + pub fn send_and_confirm_transaction( + &self, + instructions: &[Instruction], + signing_keypairs: &T, + ) -> Result { + 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); + + self.solana_client.send_and_confirm_transaction(&transaction) + } + + pub fn get_account_data_pack( + &self, + owner_program_id: &Pubkey, + account_key: &Pubkey, + ) -> Result, ClientError> { + 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 account_info.data.is_empty() { + panic!("Account {} is empty", account_key); + } + if account_info.owner != *owner_program_id { + panic!("Invalid account owner for {}: expected {}, actual {}", + account_key, owner_program_id, account_info.owner); + } + + let account: T = T::unpack(&account_info.data).unwrap(); //try_from_slice_unchecked(&account_info.data).unwrap(); + if !account.is_initialized() { + panic!("Unitialized account {}", account_key); + } + Ok(Some(account)) + } else { + Ok(None) + } + } + + pub fn get_account_data( + &self, + owner_program_id: &Pubkey, + account_key: &Pubkey, + ) -> Result, ClientError> { + 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 account_info.data.is_empty() { + panic!("Account {} is empty", account_key); + } + if account_info.owner != *owner_program_id { + panic!("Invalid account owner for {}: expected {}, actual {}", + account_key, owner_program_id, account_info.owner); + } + + let account: T = try_from_slice_unchecked(&account_info.data).unwrap(); + if !account.is_initialized() { + panic!("Unitialized account {}", account_key); + } + Ok(Some(account)) + } else { + Ok(None) + } + } + pub fn account_exists(&self, address: &Pubkey) -> bool { self.solana_client.get_account(address).is_ok() } @@ -124,7 +201,10 @@ impl<'a> SplGovernanceInteractor<'a> { data: self.get_realm_v2(realm_name).unwrap(), //max_voter_weight_addin_address: addin_opt, // voter_weight_addin_address: addin_opt, - max_voter_weight_record_address: None, + //max_voter_weight_record_address: RefCell::new(None), + _settings: RefCell::new(RealmSettings { + max_voter_weight_record_address: addin_opt, + }), } ) } diff --git a/governance-lib/src/governance.rs b/governance-lib/src/governance.rs index 153dd20..3e012a6 100644 --- a/governance-lib/src/governance.rs +++ b/governance-lib/src/governance.rs @@ -14,10 +14,10 @@ use { }, spl_governance::{ state::{ - governance::GovernanceV2, + governance::{GovernanceConfig, GovernanceV2}, proposal::{VoteType, ProposalV2, get_proposal_address}, }, - instruction::create_proposal, + instruction::{create_proposal, set_governance_config}, }, solana_client::{ client_error::ClientError, @@ -34,8 +34,11 @@ pub struct Governance<'a> { impl<'a> Governance<'a> { pub fn get_interactor(&self) -> &SplGovernanceInteractor<'a> {self.realm.interactor} - pub fn get_proposal_count(&self) -> u32 { - self.data.proposals_count + pub fn get_proposals_count(&self) -> u32 { + let data: GovernanceV2 = self.get_interactor().get_account_data( + &self.get_interactor().spl_governance_program_address, + &self.address).unwrap().unwrap(); + data.proposals_count } pub fn get_proposal_address(&self, proposal_index: u32) -> Pubkey { @@ -51,6 +54,14 @@ impl<'a> Governance<'a> { ProposalV2::deserialize(&mut dt).unwrap() } + // Note: Only governance PDA via a proposal can authorize change to its own config + pub fn set_governance_config_instruction(&self, config: GovernanceConfig) -> Instruction { + set_governance_config( + &self.get_interactor().spl_governance_program_address, + &self.address, + config) + } + pub fn create_proposal<'b:'a>(&'b self, create_authority: &Keypair, token_owner: &TokenOwner, proposal_name: &str, proposal_description: &str, proposal_index: u32) -> Result,ClientError> { let proposal_address: Pubkey = self.get_proposal_address(proposal_index); let payer = &self.get_interactor().payer; diff --git a/governance-lib/src/lib.rs b/governance-lib/src/lib.rs index d7c86ff..885b573 100644 --- a/governance-lib/src/lib.rs +++ b/governance-lib/src/lib.rs @@ -6,6 +6,7 @@ pub mod proposal; pub mod token_owner; pub mod addin_fixed_weights; +pub mod addin_vesting; #[cfg(test)] mod tests { diff --git a/governance-lib/src/proposal.rs b/governance-lib/src/proposal.rs index 82d8047..ad4a6e6 100644 --- a/governance-lib/src/proposal.rs +++ b/governance-lib/src/proposal.rs @@ -4,26 +4,33 @@ use { governance::Governance, token_owner::TokenOwner, }, + borsh::BorshDeserialize, solana_sdk::{ pubkey::Pubkey, - instruction::Instruction, + instruction::{AccountMeta, Instruction}, transaction::Transaction, signer::{Signer, keypair::Keypair}, signature::Signature, + program_error::ProgramError, }, spl_governance::{ state::{ - enums::ProposalState, + enums::{ProposalState, TransactionExecutionStatus}, vote_record::{Vote, VoteChoice}, proposal::{ProposalV2}, + proposal_transaction::{ProposalTransactionV2, InstructionData, get_proposal_transaction_address}, }, instruction::{ cast_vote, sign_off_proposal, + insert_transaction, + remove_transaction, + execute_transaction, }, }, solana_client::{ client_error::{ClientError, Result as ClientResult}, + rpc_config::RpcSendTransactionConfig, }, }; @@ -43,6 +50,159 @@ impl<'a> Proposal<'a> { Ok(data.state) } + pub fn get_proposal_transaction_address(&self, option_index: u8, index: u16) -> Pubkey { + get_proposal_transaction_address( + &self.get_interactor().spl_governance_program_address, + &self.address, + &option_index.to_le_bytes(), + &index.to_le_bytes()) + } + + pub fn get_proposal_transaction_data(&self, option_index: u8, index: u16) -> ClientResult> { + let transaction_pubkey: Pubkey = self.get_proposal_transaction_address(option_index, index); + + //let mut dt: &[u8] = &self.get_interactor().solana_client.get_account_data(&transaction_pubkey)?; + //Ok(ProposalTransactionV2::deserialize(&mut dt).unwrap()) + self.get_interactor().get_account_data::( + &self.get_interactor().spl_governance_program_address, + &self.get_proposal_transaction_address(option_index, index)) + } + +// pub fn finalize_vote(&self, sign_authority: &Keypair, token_owner: &TokenOwner) -> ClientResult; + + pub fn insert_transaction_instruction(&self, authority: &Pubkey, option_index: u8, index: u16, hold_up_time: u32, instructions: Vec) -> Result { + Ok( + insert_transaction( + &self.get_interactor().spl_governance_program_address, + &self.governance.address, + &self.address, + &self.token_owner_record, + authority, + &self.get_interactor().payer.pubkey(), + + option_index, + index, + hold_up_time, + instructions, + ) + ) + } + + pub fn insert_transaction(&self, authority: &Keypair, option_index: u8, index: u16, hold_up_time: u32, instructions: Vec) -> ClientResult { + let payer = self.get_interactor().payer; + + let transaction: Transaction = Transaction::new_signed_with_payer( + &[ + insert_transaction( + &self.get_interactor().spl_governance_program_address, + &self.governance.address, + &self.address, + &self.token_owner_record, + &authority.pubkey(), + &payer.pubkey(), + + option_index, + index, + hold_up_time, + instructions, + ), + ], + Some(&payer.pubkey()), + &[ + payer, + authority, + ], + self.get_interactor().solana_client.get_latest_blockhash().unwrap(), + ); + + self.get_interactor().solana_client.send_and_confirm_transaction(&transaction) + } + + pub fn remove_transaction(&self, authority: &Keypair, option_index: u8, index: u16, beneficiary: &Pubkey) -> ClientResult { + let payer = self.get_interactor().payer; + + let transaction: Transaction = Transaction::new_signed_with_payer( + &[ + remove_transaction( + &self.get_interactor().spl_governance_program_address, + &self.address, + &self.token_owner_record, + &authority.pubkey(), + &self.get_proposal_transaction_address(option_index, index), + beneficiary, + ), + ], + Some(&payer.pubkey()), + &[ + payer, + authority, + ], + self.get_interactor().solana_client.get_latest_blockhash().unwrap(), + ); + + self.get_interactor().solana_client.send_and_confirm_transaction(&transaction) + } + + pub fn execute_transactions(&self, option_index: u8) -> ClientResult> { + let mut signatures = vec!(); + let mut index = 0; + + while let Some(proposal_transaction) = self.get_proposal_transaction_data(option_index, index)? { + if proposal_transaction.execution_status == TransactionExecutionStatus::None { + println!("Execute proposal transaction: {} {} =====================", option_index, index); + signatures.push(self._execute_transaction(&proposal_transaction)?); + } + index += 1; + } + Ok(signatures) + } + + pub fn execute_transaction(&self, option_index: u8, index: u16) -> ClientResult { + let proposal_transaction = self.get_proposal_transaction_data(option_index, index)?.unwrap(); + self._execute_transaction(&proposal_transaction) + } + + fn _execute_transaction(&self, proposal_transaction: &ProposalTransactionV2) -> ClientResult { + let payer = self.get_interactor().payer; + println!("Proposal transaction: {:?}", proposal_transaction); + let mut accounts = vec!(); + for instruction in &proposal_transaction.instructions { + accounts.push(AccountMeta::new_readonly(instruction.program_id, false)); + accounts.extend(instruction.accounts.iter() + .map(|a| if a.is_writable { + AccountMeta::new(a.pubkey, a.is_signer && a.pubkey != self.governance.address) + } else { + AccountMeta::new_readonly(a.pubkey, a.is_signer && a.pubkey != self.governance.address) + })); + } + + println!("Governance: {}", self.governance.address); + println!("Proposal: {}", self.address); + println!("Execute transaction with accounts {:?}", accounts); + + let transaction: Transaction = Transaction::new_signed_with_payer( + &[ + execute_transaction( + &self.get_interactor().spl_governance_program_address, + &self.governance.address, + &self.address, + &self.get_proposal_transaction_address( + proposal_transaction.option_index, + proposal_transaction.transaction_index), + &self.governance.address, // Dummy account to call execute_transaction (bug in instruction.rs implementation) + &accounts, + ), + ], + Some(&payer.pubkey()), + &[ + payer, + ], + self.get_interactor().solana_client.get_latest_blockhash().unwrap(), + ); + + self.get_interactor().solana_client.send_and_confirm_transaction(&transaction) + } + pub fn sign_off_proposal(&self, sign_authority: &Keypair, token_owner: &TokenOwner) -> ClientResult { let payer = self.get_interactor().payer; @@ -99,7 +259,7 @@ impl<'a> Proposal<'a> { &self.governance.realm.community_mint, &payer.pubkey(), voter.voter_weight_record_address, - self.governance.realm.max_voter_weight_record_address, + self.governance.realm.settings().max_voter_weight_record_address, vote, ); diff --git a/governance-lib/src/realm.rs b/governance-lib/src/realm.rs index 514c25f..9834c79 100644 --- a/governance-lib/src/realm.rs +++ b/governance-lib/src/realm.rs @@ -6,27 +6,56 @@ use { }, borsh::BorshDeserialize, solana_sdk::{ + borsh::try_from_slice_unchecked, pubkey::Pubkey, instruction::Instruction, transaction::Transaction, signer::{Signer, keypair::Keypair}, + signature::Signature, }, spl_governance::{ state::{ - realm::RealmV2, + enums::MintMaxVoteWeightSource, + realm::{RealmV2, SetRealmAuthorityAction}, + realm_config::{RealmConfigAccount, get_realm_config_address}, token_owner_record::{TokenOwnerRecordV2, get_token_owner_record_address}, governance::{GovernanceConfig, GovernanceV2, get_governance_address}, }, instruction::{ create_token_owner_record, create_governance, + create_mint_governance, + set_realm_config, + set_realm_authority, }, }, solana_client::{ client_error::ClientError, }, + std::cell::{RefCell, Ref, RefMut}, }; +pub struct RealmConfig { + pub council_token_mint: Option, + pub community_voter_weight_addin: Option, + pub max_community_voter_weight_addin: Option, + pub min_community_weight_to_create_governance: u64, + pub community_mint_max_vote_weight_source: MintMaxVoteWeightSource, +} + +#[derive(Debug)] +pub struct RealmSettings { + pub max_voter_weight_record_address: Option, +} + +impl RealmSettings { + pub fn default() -> Self { + Self { + max_voter_weight_record_address: None, + } + } +} + #[derive(Debug)] pub struct Realm<'a> { // authority: &'a Keypair, @@ -36,15 +65,14 @@ pub struct Realm<'a> { pub community_mint: Pubkey, pub data: RealmV2, //max_voter_weight_addin_address: Option, - pub max_voter_weight_record_address: Option, // voter_weight_addin_address: Option, + pub _settings: RefCell, } impl<'a> Realm<'a> { - pub fn set_max_voter_weight_record_address(&mut self, max_voter_weight_record_address: Option) { - self.max_voter_weight_record_address = max_voter_weight_record_address; - } + pub fn settings(&self) -> Ref {self._settings.borrow()} + pub fn settings_mut(&self) -> RefMut {self._settings.borrow_mut()} pub fn get_token_owner_record_address(&self, token_owner: &Pubkey) -> Pubkey { get_token_owner_record_address(&self.interactor.spl_governance_program_address, &self.address, &self.community_mint, token_owner) @@ -87,6 +115,7 @@ impl<'a> Realm<'a> { TokenOwner { realm: self, //authority: token_owner_keypair, + token_owner: *token_owner, token_owner_record_address, token_owner_record: self.get_token_owner_record_v2(token_owner), // voter_weight_record_authority: None, @@ -107,7 +136,9 @@ impl<'a> Realm<'a> { GovernanceV2::deserialize(&mut dt).unwrap() } - pub fn create_governance<'b:'a>(&'b self, create_authority: &Keypair, token_owner: &TokenOwner, governed_account_pubkey: &Pubkey, gov_config: GovernanceConfig) -> Result,ClientError> { + pub fn create_governance<'b:'a>(&'b self, create_authority: &Keypair, token_owner: &TokenOwner, + governed_account_pubkey: &Pubkey, gov_config: GovernanceConfig) -> Result,ClientError> + { let governance_pubkey: Pubkey = self.get_governance_address(governed_account_pubkey); if !self.interactor.account_exists(&governance_pubkey) { @@ -146,4 +177,130 @@ impl<'a> Realm<'a> { } ) } + + pub fn create_mint_governance<'b:'a>(&'b self, create_authority: &Keypair, token_owner: &TokenOwner, + governed_mint: &Pubkey, governed_mint_authority: &Keypair, gov_config: GovernanceConfig, + transfer_mint_authorities: bool) -> Result,ClientError> + { + let governance_pubkey: Pubkey = self.get_governance_address(governed_mint); + + if !self.interactor.account_exists(&governance_pubkey) { + let create_mint_governance_instruction: Instruction = + create_mint_governance( + &self.interactor.spl_governance_program_address, + &self.address, + &governed_mint, + &governed_mint_authority.pubkey(), + &token_owner.token_owner_record_address, + &self.interactor.payer.pubkey(), + &create_authority.pubkey(), // realm_authority OR token_owner authority + token_owner.voter_weight_record_address, + gov_config, + transfer_mint_authorities, + ); + + let transaction: Transaction = + Transaction::new_signed_with_payer( + &[ + create_mint_governance_instruction, + ], + Some(&self.interactor.payer.pubkey()), + &[ + create_authority, + self.interactor.payer, + ], + self.interactor.solana_client.get_latest_blockhash().unwrap(), + ); + + self.interactor.solana_client.send_and_confirm_transaction(&transaction).unwrap(); + } + Ok( + Governance { + realm: self, + address: governance_pubkey, + data: self.get_governance_v2(governed_mint) + } + ) + } + + pub fn get_realm_config(&self) -> Result { + let realm_config_address = get_realm_config_address(&self.interactor.spl_governance_program_address, &self.address); + let realm_config_data = self.interactor.solana_client.get_account_data(&realm_config_address)?; + let realm_config: RealmConfigAccount = try_from_slice_unchecked(&realm_config_data).unwrap(); + Ok(realm_config) + } + + pub fn set_realm_config_instruction(&self, realm_authority: &Pubkey, realm_config: &RealmConfig) -> Instruction { + set_realm_config( + &self.interactor.spl_governance_program_address, + &self.address, + realm_authority, + realm_config.council_token_mint, + &self.interactor.payer.pubkey(), + realm_config.community_voter_weight_addin, + realm_config.max_community_voter_weight_addin, + realm_config.min_community_weight_to_create_governance, + realm_config.community_mint_max_vote_weight_source.clone(), + ) + } + + pub fn set_realm_config(&self, realm_authority: &Keypair, realm_config: &RealmConfig) -> Result<(),ClientError> { + let transaction: Transaction = + Transaction::new_signed_with_payer( + &[ + set_realm_config( + &self.interactor.spl_governance_program_address, + &self.address, + &realm_authority.pubkey(), + realm_config.council_token_mint, + &self.interactor.payer.pubkey(), + realm_config.community_voter_weight_addin, + realm_config.max_community_voter_weight_addin, + realm_config.min_community_weight_to_create_governance, + realm_config.community_mint_max_vote_weight_source.clone(), + ), + ], + Some(&self.interactor.payer.pubkey()), + &[ + realm_authority, + self.interactor.payer, + ], + self.interactor.solana_client.get_latest_blockhash().unwrap(), + ); + + self.interactor.solana_client.send_and_confirm_transaction(&transaction).unwrap(); + Ok(()) + } + + pub fn set_realm_authority_instruction(&self, realm_authority: &Pubkey, new_realm_authority: Option<&Pubkey>, action: SetRealmAuthorityAction) -> Instruction { + set_realm_authority( + &self.interactor.spl_governance_program_address, + &self.address, + &realm_authority, + new_realm_authority, + action + ) + } + pub fn set_realm_authority(&self, realm_authority: &Keypair, new_realm_authority: Option<&Pubkey>, action: SetRealmAuthorityAction) -> Result { + let transaction: Transaction = + Transaction::new_signed_with_payer( + &[ + set_realm_authority( + &self.interactor.spl_governance_program_address, + &self.address, + &realm_authority.pubkey(), + new_realm_authority, + action + ), + ], + Some(&self.interactor.payer.pubkey()), + &[ + realm_authority, + self.interactor.payer, + ], + self.interactor.solana_client.get_latest_blockhash()?, + ); + + self.interactor.solana_client.send_and_confirm_transaction(&transaction) + } } diff --git a/governance-lib/src/token_owner.rs b/governance-lib/src/token_owner.rs index 942cead..683e948 100644 --- a/governance-lib/src/token_owner.rs +++ b/governance-lib/src/token_owner.rs @@ -1,19 +1,65 @@ use { - crate::realm::Realm, - solana_sdk::pubkey::Pubkey, - spl_governance::state::token_owner_record::TokenOwnerRecordV2, + crate::{ + client::SplGovernanceInteractor, + realm::Realm, + }, + solana_sdk::{ + instruction::Instruction, + transaction::Transaction, + signer::{Signer, keypair::Keypair}, + pubkey::Pubkey, + signature::Signature, + }, + spl_governance::{ + state::token_owner_record::TokenOwnerRecordV2, + instruction::set_governance_delegate, + }, + solana_client::{ + client_error::{ClientError, Result as ClientResult}, + }, + std::cell::RefCell, }; #[derive(Debug)] pub struct TokenOwner<'a> { pub realm: &'a Realm<'a>, + pub token_owner: Pubkey, pub token_owner_record_address: Pubkey, pub token_owner_record: TokenOwnerRecordV2, pub voter_weight_record_address: Option, } impl<'a> TokenOwner<'a> { + fn get_interactor(&self) -> &SplGovernanceInteractor<'a> {self.realm.interactor} + pub fn set_voter_weight_record_address(&mut self, voter_weight_record_address: Option) { self.voter_weight_record_address = voter_weight_record_address; } + + pub fn set_delegate(&self, authority: &Keypair, new_delegate: &Option) -> ClientResult { + let payer = self.get_interactor().payer; + + let transaction: Transaction = Transaction::new_signed_with_payer( + &[ + set_governance_delegate( + &self.get_interactor().spl_governance_program_address, + &authority.pubkey(), + &self.realm.address, + &self.realm.community_mint, + &self.token_owner, + new_delegate, + ), + ], + Some(&payer.pubkey()), + &[ + payer, + authority, + ], + self.get_interactor().solana_client.get_latest_blockhash().unwrap(), + ); + + println!("Transaction: {:?}", transaction); + + self.get_interactor().solana_client.send_and_confirm_transaction(&transaction) + } } diff --git a/launch-script/Cargo.toml b/launch-script/Cargo.toml new file mode 100644 index 0000000..4144ef4 --- /dev/null +++ b/launch-script/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "launch-script" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +solana-sdk = "1.9" +solana-client = "1.9" +borsh = "0.9.1" +spl-token = { version = "3.3", path = "../solana-program-library/token/program", features = [ "no-entrypoint" ] } +spl-associated-token-account = { path = "../solana-program-library/associated-token-account/program", features = [ "no-entrypoint" ] } +spl-governance = { version = "2.2", path = "../solana-program-library/governance/program" } +spl-governance-addin-api= { version = "0.1.1", path = "../solana-program-library/governance/addin-api"} +spl-governance-addin-mock = { version = "0.1", path = "../solana-program-library/governance/addin-mock/program" } + +spl-governance-addin-fixed-weights = { version = "0.1", path = "../addin-fixed-weights/program" } +spl-governance-addin-vesting = { path = "../addin-vesting/program" } + +governance-lib = { path = "../governance-lib" } diff --git a/launch-script/README.md b/launch-script/README.md new file mode 100644 index 0000000..b0b16a6 --- /dev/null +++ b/launch-script/README.md @@ -0,0 +1,25 @@ +# spl-governance-dev + + +## Preparing steps: +``` +solana-keygen new -f -o governance-test-scripts/community_mint.keypair --no-bip39-passphrase + +solana config set -u http://localhost:8899 +solana airdrop 1000 artifacts/payer.keypair +solana program deploy -k artifacts/payer.keypair solana-program-library/target/deploy/spl_governance.so +solana program deploy -k artifacts/payer.keypair target/deploy/spl_governance_addin_fixed_weights.so +solana program deploy -k artifacts/payer.keypair target/deploy/spl_governance_addin_vesting.so +``` + + + + +Snippets: +1. Forced send transaction (in case of fail you can see it on Solana blockexplorer): +``` + //self.get_interactor().solana_client.send_and_confirm_transaction(&transaction) + self.get_interactor().solana_client.send_and_confirm_transaction_with_spinner_and_config(&transaction, + self.get_interactor().solana_client.commitment(), + RpcSendTransactionConfig {skip_preflight: true, ..RpcSendTransactionConfig::default()}) +``` diff --git a/launch-script/community_mint.keypair b/launch-script/community_mint.keypair new file mode 100644 index 0000000..8f35d12 --- /dev/null +++ b/launch-script/community_mint.keypair @@ -0,0 +1 @@ +[180,246,24,202,72,140,36,173,163,183,130,48,155,133,224,186,161,154,90,88,186,160,144,93,138,82,157,174,32,159,8,99,136,50,169,190,75,38,89,39,108,43,138,119,25,8,211,221,71,57,205,213,197,176,104,202,133,194,223,78,167,159,242,1] \ No newline at end of file diff --git a/launch-script/governance.keypair b/launch-script/governance.keypair new file mode 100644 index 0000000..024f6da --- /dev/null +++ b/launch-script/governance.keypair @@ -0,0 +1 @@ +[30,135,165,225,246,149,235,136,96,115,96,102,141,150,184,54,248,59,226,48,254,170,60,189,238,181,158,56,201,150,109,155,18,248,22,87,178,226,232,120,58,6,190,91,113,53,102,148,88,60,249,220,193,45,147,20,203,202,129,17,79,100,15,126] \ No newline at end of file diff --git a/launch-script/src/main.rs b/launch-script/src/main.rs new file mode 100644 index 0000000..0171fe7 --- /dev/null +++ b/launch-script/src/main.rs @@ -0,0 +1,416 @@ +mod tokens; +mod token_distribution; + +use crate::{ + tokens::create_mint, +}; +use solana_sdk::{ + pubkey::Pubkey, + signer::{ + Signer, + keypair::read_keypair_file, + }, + transaction::Transaction, + system_instruction, + rent::Rent, +}; + +use spl_governance::{ + state::{ + enums::{ + MintMaxVoteWeightSource, + VoteThresholdPercentage, + VoteTipping, + ProposalState, + }, + governance::{ + GovernanceConfig, + GovernanceV2, + }, + realm::SetRealmAuthorityAction, + }, +}; + +//use spl_governance_addin_fixed_weights::{ +// instruction::{ +// get_max_voter_weight_address, +// get_voter_weight_address, +// } +//}; + +use spl_governance_addin_vesting::state::VestingSchedule; + +// mod tokens; + +use governance_lib::{ + client::SplGovernanceInteractor, + realm::{RealmConfig, Realm}, + governance::Governance, + proposal::Proposal, + token_owner::TokenOwner, + addin_fixed_weights::AddinFixedWeights, + addin_vesting::AddinVesting, +}; + +use std::cell::RefCell; + +use crate::token_distribution::DISTRIBUTION_LIST; +const ADDITIONAL_SUPPLY: u64 = 10_000_000; + +const GOVERNANCE_KEY_FILE_PATH: &str = "solana-program-library/target/deploy/spl_governance-keypair.json"; +const VOTER_WEIGHT_ADDIN_KEY_FILE_PATH: &str = "target/deploy/spl_governance_addin_fixed_weights-keypair.json"; +const VESTING_ADDIN_KEY_FILE_PATH: &str = "target/deploy/spl_governance_addin_vesting-keypair.json"; +const COMMUTINY_MINT_KEY_FILE_PATH: &str = "governance-test-scripts/community_mint.keypair"; +const GOVERNED_MINT_KEY_FILE_PATH: &str = "governance-test-scripts/governance.keypair"; +const PAYER_KEY_FILE_PATH: &str = "artifacts/payer.keypair"; +const CREATOR_KEY_FILE_PATH: &str = "artifacts/creator.keypair"; + +const VOTERS_KEY_FILE_PATH: [&str;5] = [ + "artifacts/voter1.keypair", + "artifacts/voter2.keypair", + "artifacts/voter3.keypair", + "artifacts/voter4.keypair", + "artifacts/voter5.keypair", +]; + +// const REALM_NAME: &str = "Test Realm"; +const REALM_NAME: &str = "Test_Realm_7"; +// const REALM_NAME: &str = "Test Realm 6"; +const PROPOSAL_NAME: &str = "Proposal To Vote"; +const PROPOSAL_DESCRIPTION: &str = "proposal_description"; + +fn main() { + + let program_id = read_keypair_file(GOVERNANCE_KEY_FILE_PATH).unwrap().pubkey(); + println!("Governance Program Id: {}", program_id); + + let payer_keypair = read_keypair_file(PAYER_KEY_FILE_PATH).unwrap(); + println!("Payer Pubkey: {}", payer_keypair.pubkey()); + + let creator_keypair = read_keypair_file(CREATOR_KEY_FILE_PATH).unwrap(); + println!("Creator Pubkey: {}", creator_keypair.pubkey()); + + let community_keypair = read_keypair_file(COMMUTINY_MINT_KEY_FILE_PATH).unwrap(); + let community_pubkey = community_keypair.pubkey(); + println!("Community Token Mint Pubkey: {}", community_pubkey); + + let voter_weight_addin_pubkey = read_keypair_file(VOTER_WEIGHT_ADDIN_KEY_FILE_PATH).unwrap().pubkey(); + println!("Voter Weight Addin Pubkey: {}", voter_weight_addin_pubkey); + + let vesting_addin_pubkey = read_keypair_file(VESTING_ADDIN_KEY_FILE_PATH).unwrap().pubkey(); + println!("Vesting Addin Pubkey: {}", vesting_addin_pubkey); + + let governed_account_pubkey = read_keypair_file(GOVERNED_MINT_KEY_FILE_PATH).unwrap().pubkey(); + println!("Governed Account (Mint) Pubkey: {}", governed_account_pubkey); + + let mut voter_keypairs = vec!(); + for (i, file) in VOTERS_KEY_FILE_PATH.iter().enumerate() { + let keypair = read_keypair_file(file).unwrap(); + println!("Voter{} Pubkey: {}", i, keypair.pubkey()); + voter_keypairs.push(keypair); + } + + let interactor = SplGovernanceInteractor::new( + "http://localhost:8899", + program_id, + voter_weight_addin_pubkey, + &payer_keypair); + // let interactor = SplGovernanceInteractor::new("https://api.devnet.solana.com", program_id, voter_weight_addin_pubkey); + + let mint = interactor.get_account_data_pack::(&spl_token::id(), &community_pubkey).unwrap(); + if let Some(_mint) = mint { +// if !mint.mint_authority.contains(&payer_keypair.pubkey()) { +// panic!("Invalid mint authority: actual {:?}, expected {}", mint.mint_authority, creator_keypair.pubkey()); +// } + } else { + let result = create_mint( + &interactor.solana_client, + &payer_keypair, + &community_keypair, + &creator_keypair.pubkey(), + None, + 6).unwrap(); + println!("Created community mint: {}", result); + } + + let realm: Realm = interactor.create_realm( + &creator_keypair, + &community_pubkey, + Some(voter_weight_addin_pubkey), + REALM_NAME).unwrap(); + println!("{:?}", realm); + println!("Realm Pubkey: {}", interactor.get_realm_address(REALM_NAME)); + + let fixed_weight_addin = AddinFixedWeights::new(&interactor, voter_weight_addin_pubkey); + let result = fixed_weight_addin.setup_max_voter_weight_record(&realm); + println!("VoterWeightAddin.setup_max_voter_weight_record = {:?}", result); + realm.settings_mut().max_voter_weight_record_address = Some(result.unwrap()); + + let vesting_addin = AddinVesting::new(&interactor, vesting_addin_pubkey); + + //let mut creator_token_owner: TokenOwner = realm.create_token_owner_record(&creator_keypair.pubkey()).unwrap(); + //let creator_voter_weight = fixed_weight_addin.setup_voter_weight_record(&realm, &creator_keypair.pubkey()).unwrap(); + //creator_token_owner.set_voter_weight_record_address(Some(creator_voter_weight)); + //creator_token_owner.set_voter_weight_record_address(Some(creator_keypair.pubkey())); + + let mut token_owners = vec!(); + for (i, keypair) in voter_keypairs.iter().enumerate() { + let mut token_owner: TokenOwner = realm.create_token_owner_record(&keypair.pubkey()).unwrap(); + let voter_weight_record = fixed_weight_addin.setup_voter_weight_record(&realm, &keypair.pubkey()).unwrap(); + token_owner.set_voter_weight_record_address(Some(voter_weight_record)); + println!("Token Owner {} \n{:?}, voter_weight_record: {}", i, token_owner, voter_weight_record); + token_owners.push(token_owner); + } + + let result = token_owners[0].set_delegate(&voter_keypairs[0], &Some(creator_keypair.pubkey())).unwrap(); + println!("Set delegate for voter[0]: {:?}", result); + + let gov_config: GovernanceConfig = + GovernanceConfig { + vote_threshold_percentage: VoteThresholdPercentage::YesVote(60), + min_community_weight_to_create_proposal: 10, + min_transaction_hold_up_time: 0, + max_voting_time: 78200, + vote_tipping: VoteTipping::Strict, + proposal_cool_off_time: 0, + min_council_weight_to_create_proposal: 0, + }; + + let governance: Governance = realm.create_governance( + &creator_keypair, + &token_owners[0], + &governed_account_pubkey, + gov_config).unwrap(); + println!("{:?}", governance); + + // STEP 2: Pass Token and Realm under governance + // transaction if already correct authority) + let mut instructions = vec!(); + let mint = interactor.get_account_data_pack::(&spl_token::id(), &community_pubkey).unwrap().unwrap(); + if mint.mint_authority.contains(&creator_keypair.pubkey()) { + instructions.push( + spl_token::instruction::set_authority( + &spl_token::id(), + &community_pubkey, + Some(&governance.address), + spl_token::instruction::AuthorityType::MintTokens, + &creator_keypair.pubkey(), + &[], + ).unwrap() + ); + } + let realm_data = interactor.get_account_data::(&program_id, &realm.address).unwrap().unwrap(); + if realm_data.authority == Some(creator_keypair.pubkey()) { + instructions.push( + realm.set_realm_authority_instruction( + &creator_keypair.pubkey(), + Some(&governance.address), + SetRealmAuthorityAction::SetChecked, + ) + ); + } + if !instructions.is_empty() { + let transaction: Transaction = + Transaction::new_signed_with_payer( + &instructions, + Some(&payer_keypair.pubkey()), + &[ + &creator_keypair, + &payer_keypair, + ], + interactor.solana_client.get_latest_blockhash().unwrap(), + ); + interactor.solana_client.send_and_confirm_transaction(&transaction).unwrap(); + } + + // ========================================================================= + // Create TGE proposal (Token Genesis Event) + // ========================================================================= + + let proposal_number: u32 = governance.get_proposals_count(); + let proposal: Proposal = governance.create_proposal( + &creator_keypair, + &token_owners[0], + &format!("{} {}", PROPOSAL_NAME, proposal_number), + PROPOSAL_DESCRIPTION, + proposal_number).unwrap(); + println!("{:?}", proposal); + + // let result = interactor.add_signatory(&realm, &governance, &proposal, &token_owner); + // println!("Add signatory {:?}", result); + + let governance_token_account = spl_associated_token_account::get_associated_token_address_with_program_id( + &governance.address, + &community_pubkey, + &spl_token::id(), + ); + println!("Governance address: {}", governance.address); + println!("Governance token account: {}", governance_token_account); + if !interactor.account_exists(&governance_token_account) { + let transaction: Transaction = + Transaction::new_signed_with_payer( + &[ + spl_associated_token_account::create_associated_token_account( + &payer_keypair.pubkey(), + &governance.address, + &community_pubkey, + ), + ], + Some(&payer_keypair.pubkey()), + &[ + &payer_keypair, + ], + interactor.solana_client.get_latest_blockhash().unwrap(), + ); + let signature = interactor.solana_client.send_and_confirm_transaction(&transaction).unwrap(); + println!("Create associated token account {}", signature); + } + + let total_amount = DISTRIBUTION_LIST.iter().map(|(_, amount)| amount).sum::() + ADDITIONAL_SUPPLY; + proposal.insert_transaction( + &creator_keypair, + 0, 0, 0, + vec![ + spl_token::instruction::mint_to( + &spl_token::id(), + &community_pubkey, + &governance_token_account, + &governance.address, &[], + total_amount, + ).unwrap().into(), + ], + ).unwrap(); + + for (i, (owner, amount)) in DISTRIBUTION_LIST.iter().enumerate() { + let seed: String = format!("{}_vesting_{}", REALM_NAME, i); + let vesting_token_account = Pubkey::create_with_seed(&creator_keypair.pubkey(), &seed, &spl_token::id()).unwrap(); + // TODO Calculate schedule + let schedule = vec!(VestingSchedule { release_time: 0, amount: *amount }); + println!("{}, Voter {}, amount {}, token_account {}", i, owner, amount, vesting_token_account); + + let mut instructions = vec!(); + if !interactor.account_exists(&vesting_token_account) { + instructions.extend([ + system_instruction::create_account_with_seed( + &payer_keypair.pubkey(), // from + &vesting_token_account, // to + &creator_keypair.pubkey(), // base + &seed, // seed + Rent::default().minimum_balance(165), // lamports + 165, // space + &spl_token::id(), // owner + ), + spl_token::instruction::initialize_account( + &spl_token::id(), + &vesting_token_account, + &community_pubkey, + &vesting_addin.find_vesting_account(&vesting_token_account), + ).unwrap(), + ]); + } + instructions.push( + proposal.insert_transaction_instruction( + &creator_keypair.pubkey(), + 0, (i+1).try_into().unwrap(), 0, + vec![ + vesting_addin.deposit_with_realm_instruction( + &governance.address, // source_token_authority + &governance_token_account, // source_token_account + &owner, // vesting_owner + &vesting_token_account, // vesting_token_account + schedule, // schedule + &realm, // realm + ).unwrap().into(), + ], + ).unwrap(), + ); + + let result = interactor.send_and_confirm_transaction(&instructions, &[&creator_keypair]).unwrap(); + println!(" created: {}", result); + } + + // Change to other VoterWeight addin + proposal.insert_transaction( + &creator_keypair, + 0, (DISTRIBUTION_LIST.len()+1).try_into().unwrap(), 0, + vec![ + realm.set_realm_config_instruction( + &governance.address, // we already passed realm under governance + &RealmConfig { + council_token_mint: None, + community_voter_weight_addin: Some(vesting_addin_pubkey), + max_community_voter_weight_addin: None, + min_community_weight_to_create_governance: 1, + community_mint_max_vote_weight_source: MintMaxVoteWeightSource::FULL_SUPPLY_FRACTION, + } + ).into(), + ], + ).unwrap(); + + // Change Governance config + proposal.insert_transaction( + &creator_keypair, + 0, (DISTRIBUTION_LIST.len()+2).try_into().unwrap(), 0, + vec![ + governance.set_governance_config_instruction( + GovernanceConfig { + vote_threshold_percentage: VoteThresholdPercentage::YesVote(2), + min_community_weight_to_create_proposal: 3*1000_000, + min_transaction_hold_up_time: 0, + max_voting_time: 1*60, // 3*24*3600, + vote_tipping: VoteTipping::Disabled, + proposal_cool_off_time: 0, // not implemented in the current version + min_council_weight_to_create_proposal: 0, // council token does not used + }, + ).into(), + ], + ).unwrap(); + + if proposal.get_state().unwrap() == ProposalState::Draft { + proposal.sign_off_proposal(&creator_keypair, &token_owners[0]).unwrap(); + } + + for (i, owner) in token_owners.iter().enumerate() { + let yes_no = i == 0 || i == 3 || i == 4; + let result = proposal.cast_vote(&voter_keypairs[i], owner, yes_no); + println!("CastVote {} {:?}", i, result); + } + + std::thread::sleep(std::time::Duration::from_secs(2)); + + let result = proposal.execute_transactions(0).unwrap(); + println!("Execute transactions from proposal option 0: {:?}", result); + + + // =================================================================================== + // Check correctly operation after switching to vesting-addin + // =================================================================================== + realm.settings_mut().max_voter_weight_record_address = None; + for (ref mut token_owner) in token_owners.iter_mut() { + let token_owner_pubkey = token_owner.token_owner; + let voter_weight_record = vesting_addin.get_voter_weight_record_address(&token_owner_pubkey, &realm); + token_owner.set_voter_weight_record_address(Some(voter_weight_record)); + } + + // =================================================================================== + // Create proposal + // =================================================================================== + let proposal: Proposal = governance.create_proposal( + &creator_keypair, + &token_owners[0], + "Deploy EVM", + PROPOSAL_DESCRIPTION, + governance.get_proposals_count(), + ).unwrap(); + println!("{:?}", proposal); + + if proposal.get_state().unwrap() == ProposalState::Draft { + proposal.sign_off_proposal(&creator_keypair, &token_owners[0]).unwrap(); + } + + for (i, owner) in token_owners.iter().enumerate() { + let yes_no = i == 0 || i == 3 || i == 4; + let result = proposal.cast_vote(&voter_keypairs[i], owner, yes_no); + println!("CastVote {} {:?}", i, result); + } +} diff --git a/launch-script/src/token_distribution.rs b/launch-script/src/token_distribution.rs new file mode 100644 index 0000000..1be479f --- /dev/null +++ b/launch-script/src/token_distribution.rs @@ -0,0 +1,20 @@ +macro_rules! voter_weight_array { + ($identifier:ident, [ $(($value_pubkey:expr,$value_weight:expr),)* ]) => { + /// Voter Weight List + pub static $identifier: [(::solana_sdk::pubkey::Pubkey,u64); [$(($value_pubkey,$value_weight),)*].len()] = [ + $((::solana_sdk::pubkey!($value_pubkey),$value_weight),)* + ]; + }; +} + +voter_weight_array!( + DISTRIBUTION_LIST, + [ + ("FcTRG2o9uiJQPSZhJufb6cDkYXpmdEz54m952TmireW", 9_000_000), + ("xaEuzqGFrUvjiEokdzpC2HAKotJFumGYo2YmNTS4eFZ", 2_000_000), + ("EnCMdFj7eMxMfK2KhXEfbMURWRQ3AYdwxvTNKH91GD7p", 3_000_000), + ("ACVPBh4FmfYGZu5jK6MYiAquaid2Vr3yjYFJ5RWe597v", 1_000_000), + ("A3ujb32N9vnxsMp3stRGVpxACHNmxUedgpT8knShfnhs", 5_000_000), + ] +); + diff --git a/launch-script/src/tokens.rs b/launch-script/src/tokens.rs new file mode 100644 index 0000000..5f11b43 --- /dev/null +++ b/launch-script/src/tokens.rs @@ -0,0 +1,218 @@ +use solana_sdk::{ + pubkey::{ Pubkey }, + instruction::{ Instruction }, + transaction::{ Transaction }, + signer::{ + Signer, + keypair::{ Keypair }, + }, + program_pack::{ Pack }, +}; + +use spl_token::state::{ Account, Mint }; + +use solana_client::{ + client_error::ClientError, + rpc_client::RpcClient, +}; + +pub fn create_mint(client: &RpcClient, payer: &Keypair, mint_keypair: &Keypair, + mint_authority: &Pubkey, freeze_authority: Option<&Pubkey>, decimals: u8) -> Result +{ + let transaction: Transaction = + Transaction::new_signed_with_payer( + &[ + solana_sdk::system_instruction::create_account( + &payer.pubkey(), + &mint_keypair.pubkey(), + client.get_minimum_balance_for_rent_exemption(Mint::LEN).unwrap(), + Mint::LEN as u64, + &spl_token::id(), + ), + spl_token::instruction::initialize_mint( + &spl_token::id(), + &mint_keypair.pubkey(), + mint_authority, + freeze_authority, + decimals + ).unwrap(), + ], + Some(&payer.pubkey()), + &[ + mint_keypair, + payer, + ], + client.get_latest_blockhash()?, + ); + + client.send_and_confirm_transaction(&transaction)?; + Ok(mint_keypair.pubkey()) +} + +/* +pub fn create_account(client: &RpcClient, owner_keypair: &Keypair, mint_keypair: &Keypair, mint_pubkey: &Pubkey) -> Result { + + let owner_pubkey: Pubkey = owner_keypair.pubkey(); + // let minter_pubkey: Pubkey = minter.pubkey(); + + // let mint_keypair: Keypair = + // if let Ok(keypair) = read_keypair_file(format!("/media/mich/speedwork/NeonLabs/artifacts/dev/token_mints/{}.keypair",token_info.symbol)) { + // keypair + // } else { + // return Err(()) + // }; + // let mint_pubkey = mint_keypair.pubkey(); + + // Create new Supply Account + // let supply_keypair: Keypair = Keypair::new(); + // let supply_pubkey: Pubkey = supply_keypair.pubkey(); + // println!("Supply Account: {}", supply_pubkey); + let associated_token_pubkey: Pubkey = spl_associated_token_account::get_associated_token_address(&owner_pubkey, &mint_pubkey); + + let create_supply_account_instruction: Instruction = + spl_associated_token_account::create_associated_token_account( + &owner_pubkey, + &owner_pubkey, + &mint_pubkey, + ); + // solana_sdk::system_instruction::create_account( + // &owner_pubkey, + // &associated_token_pubkey, + // // 0, + // client.get_minimum_balance_for_rent_exemption(Account::LEN).unwrap(), + // Account::LEN as u64, + // &spl_token::id(), + // ); + + let initialize_supply_account2_instruction: Instruction = + spl_token::instruction::initialize_account2( + &spl_token::id(), + &associated_token_pubkey, + &mint_pubkey, + &owner_pubkey, + ).unwrap(); + + + let transaction: Transaction = + Transaction::new_signed_with_payer( + &vec![ + create_supply_account_instruction, + // initialize_supply_account2_instruction, + ], + Some(&owner_pubkey), + &[ + owner_keypair, + // owner_keypair, + // mint_keypair + ], + client.get_latest_blockhash().unwrap(), + ); + + let result = client.send_and_confirm_transaction(&transaction); + println!("'Create Initialize Supply Account' Transaction Result: {:?}", result); + + Ok(associated_token_pubkey) + // Ok((mint_pubkey, supply_pubkey)) +} + +pub fn mint_tokens(client: &RpcClient, mint_authority: &Keypair, mint_pubkey: &Pubkey, recipient_pubkey: &Pubkey, amount: u64) { + + // let mint_authority: &Keypair = &self.mint_authority.as_ref().unwrap(); + let mint_authority_pubkey: Pubkey = mint_authority.pubkey(); + + let mint_to_instruction: Instruction = + spl_token::instruction::mint_to( + &spl_token::id(), + &mint_pubkey, + &recipient_pubkey, + &mint_authority_pubkey, + &[ &mint_authority_pubkey ], + amount, + ).unwrap(); + + let transaction: Transaction = + Transaction::new_signed_with_payer( + &vec![ mint_to_instruction ], + Some(&mint_authority_pubkey), + &[ mint_authority ], + client.get_latest_blockhash().unwrap(), + ); + + let result = client.send_and_confirm_transaction(&transaction); + println!("Mint result: {:?}", result); +} + +pub fn create_accounts_mint_liquidity(client: &RpcClient, owner_keypair: &Keypair, mint_keypair: &Keypair, mint_pubkey: &Pubkey) { + + let amount: u64 = 10_000_000_000; + // let amount: u64 = 2; + + // let client: SolClient = + // SolClient::new(NETWORK) + // .with_authority(WALLET_FILE_PATH) + // .with_mint_authority(MINTER_FILE_PATH); + + // let owner_keypair: Keypair = read_keypair_file(crate::WALLET_FILE_PATH).unwrap(); + let owner_pubkey: Pubkey = owner_keypair.pubkey(); + // println!("Solana Owner / Payer Pubkey: {}", owner_pubkey); + + let balance = client.get_balance(&owner_pubkey).unwrap(); + println!("Solana Owner / Payer Balance: {}", balance); + if balance == 0 { + println!("No Owner balance!!!"); + return; + } + + // let eth_receiver_address: EthAddress = EthAddress::from_str(receivers[0]).unwrap(); + // println!("NeonEvm Receiver Address : {}\n", eth_receiver_address); + + // let token_infos: Vec = { + // let token_list_string: String = std::fs::read_to_string("/media/mich/speedwork/NeonLabs/artifacts/tokenlist.json").unwrap(); + // let token_list: TokenList = serde_json::from_str(&token_list_string).unwrap(); + // token_list.filter_for_network(&NETWORK).unwrap() + // }; + + // for token_info in token_infos.iter() { + // let mint_pubkey: Pubkey = Pubkey::from_str(&token_info.address_spl.as_ref().unwrap()).unwrap(); + // println!("'{}' [ {} ] : Mint Pubkey: {}", token_info.name, token_info.symbol, mint_pubkey); + + // let amount_expanded: u64 = amount.expand_to_decimals(token_info.decimals).unwrap(); + // let eth_erc20_address: EthAddress = EthAddress::from_str(&token_info.address).unwrap(); + // println!("eth_erc20_address: {}", eth_erc20_address); + + // let supply_keypair: Keypair = create_liquidity(&solana_client, &owner_keypair, &minter_keypair, &mint_pubkey, token_info, amount_expanded).unwrap(); + + // transfer_liquidity(&solana_client, &NETWORK, &owner_keypair, &mint_pubkey, &supply_keypair.pubkey(), &token_info, ð_receiver_address, ð_erc20_address, amount_expanded).unwrap(); + + // let evm_loader_program_id: Pubkey = Pubkey::from_str(NETWORK.get_evm_loader_program_id()).unwrap(); + // let recipient_pubkey: Pubkey = Erc20AccountIdentity::new(ð_receiver_address,ð_erc20_address,&mint_pubkey).derive_pubkey(&evm_loader_program_id); + // let recipient_pubkey: Pubkey = client.create_derived_erc20_identity(ð_receiver_address, ð_erc20_address, &mint_pubkey); + + // match client.get_account(&recipient_pubkey) { + // Ok(_) => { + // println!("'{}' [ {} ] : ERC20 Associated Token Account: {}", token_info.name, token_info.symbol, recipient_pubkey); + // }, + // Err(_) => { + // let result = client.create_associated_token_account(&mint_pubkey, ð_receiver_address, ð_erc20_address); + // match result { + // Ok(_) => + // println!("'{}' [ {} ] : ERC20 Associated Token Account Created: {}", token_info.name, token_info.symbol, recipient_pubkey), + // Err(e) => + // println!("Transaction error while creating associated token account {} for '{}' [ {} ].\n{:?}", recipient_pubkey, token_info.name, token_info.symbol, e), + // } + // } + // }; + + let recipient_pubkey: Pubkey = create_account(&client, &owner_keypair, &mint_keypair, &mint_pubkey).unwrap(); + println!("Recipient_pubkey: {}", recipient_pubkey); + + mint_tokens(client, &owner_keypair, mint_pubkey, &recipient_pubkey, amount); + // match result { + // Ok(_) => + // println!("'{}' [ {} ] : {} Minted To {}", token_info.name, token_info.symbol, amount_expanded, recipient_pubkey), + // Err(e) => + // println!("Transaction error while minting for '{}' [ {} ].\n{:?}", token_info.name, token_info.symbol, e), + // } + // return; + // } +}*/