diff --git a/governance/README.md b/governance/README.md index 17ec211bdd4..46a1c50780e 100644 --- a/governance/README.md +++ b/governance/README.md @@ -6,7 +6,7 @@ a voting population to vote on disbursement of access or funds collectively. ## Architecture -### Accounts diagram +### Accounts diagram (Program Governance use case) ![Accounts diagram](./resources/governance-accounts.jpg) @@ -31,6 +31,22 @@ The governance program validates at creation time of the Governance account that taken under governance signed the transaction. Optionally `CreateProgramGovernance` instruction can also transfer `upgrade_authority` of the governed program to the Governance PDA at the creation time of the Governance account. +### Mint Governance account + +A mint governance account allows a mint authority to setup governance over an SPL Mint account. +The Governance program validates at creation time that the current mint authority signed the transaction to +create the governance and optionally can transfer the authority to the Governance account. +Once setup the Mint Governance allows governance participants to create Proposals which execute mint instructions for +the governed Mint. + +### Token Governance account + +A token governance account allows a token account owner to setup governance over an SPL Token account. +The Governance program validates at creation time the current owner signed the transaction to +create the governance and optionally can transfer the owner to the Governance account. +Once setup the Token Governance allows participants to create Proposals to execute transfer instructions +from the governed token account. + ### How does the authority work? Governance can handle arbitrary executions of code, but it's real power lies in the power to upgrade programs. @@ -42,6 +58,11 @@ the new program data and overwriting of the existing Program account's data with by the Solana program deploy cli command. However, in order for Governance to be useful, Governance now needs this authority. +In similar fashion for Mint and Token governances the relevant authorities to mint and transfer tokens +are transferred to the Governance account. It in turn allows participants to create and vote on Proposals +which can then execute +mint and transfer instructions for the governed accounts. + ### Proposal accounts A Proposal is an instance of a Governance created to vote on and execute given set of changes. diff --git a/governance/program/src/error.rs b/governance/program/src/error.rs index db8f24b5387..fd098df24c7 100644 --- a/governance/program/src/error.rs +++ b/governance/program/src/error.rs @@ -251,6 +251,14 @@ pub enum GovernanceError { /// Given program is not upgradable #[error("Given program is not upgradable")] ProgramNotUpgradable, + + /// Invalid token owner + #[error("Invalid token owner")] + InvalidTokenOwner, + + /// Current token owner must sign transaction + #[error("Current token owner must sign transaction")] + TokenOwnerMustSign, } impl PrintProgramError for GovernanceError { diff --git a/governance/program/src/instruction.rs b/governance/program/src/instruction.rs index fd4119786b2..f5216c71705 100644 --- a/governance/program/src/instruction.rs +++ b/governance/program/src/instruction.rs @@ -4,7 +4,7 @@ use crate::{ state::{ governance::{ get_account_governance_address, get_mint_governance_address, - get_program_governance_address, GovernanceConfig, + get_program_governance_address, get_token_governance_address, GovernanceConfig, }, proposal::get_proposal_address, proposal_instruction::{get_proposal_instruction_address, InstructionData}, @@ -133,7 +133,7 @@ pub enum GovernanceInstruction { transfer_upgrade_authority: bool, }, - /// Creates Proposal account for Instructions that will be executed at various slots in the future + /// Creates Proposal account for Instructions that will be executed at some point in the future /// /// 0. `[writable]` Proposal account. PDA seeds ['governance',governance, governing_token_mint, proposal_index] /// 1. `[writable]` Governance account @@ -202,8 +202,8 @@ pub enum GovernanceInstruction { /// Instruction index to be inserted at. index: u16, #[allow(dead_code)] - /// Slot waiting time between vote period ending and this being eligible for execution - hold_up_time: u64, + /// Waiting time (in seconds) between vote period ending and this being eligible for execution + hold_up_time: u32, #[allow(dead_code)] /// Instruction Data @@ -313,6 +313,28 @@ pub enum GovernanceInstruction { /// However the instruction would validate the current mint authority signed the transaction nonetheless transfer_mint_authority: bool, }, + + /// Creates Token Governance account which governs a token account + /// + /// 0. `[]` Realm account the created Governance belongs to + /// 1. `[writable]` Token Governance account. PDA seeds: ['token-governance', realm, governed_token] + /// 2. `[writable]` Token account governed by this Governance account + /// 3. `[signer]` Current Token account owner + /// 4. `[signer]` Payer + /// 5. `[]` SPL Token program + /// 6. `[]` System program + /// 7. `[]` Sysvar Rent + CreateTokenGovernance { + #[allow(dead_code)] + /// Governance config + config: GovernanceConfig, + + #[allow(dead_code)] + /// Indicates whether token owner should be transferred to the Governance PDA + /// If it's set to false then it can be done at a later time + /// However the instruction would validate the current token owner signed the transaction nonetheless + transfer_token_owner: bool, + }, } /// Creates CreateRealm instruction @@ -573,6 +595,42 @@ pub fn create_mint_governance( } } +/// Creates CreateTokenGovernance instruction +pub fn create_token_governance( + program_id: &Pubkey, + // Accounts + governed_token_owner: &Pubkey, + payer: &Pubkey, + // Args + config: GovernanceConfig, + transfer_token_owner: bool, +) -> Instruction { + let token_governance_address = + get_token_governance_address(program_id, &config.realm, &config.governed_account); + + let accounts = vec![ + AccountMeta::new_readonly(config.realm, false), + AccountMeta::new(token_governance_address, false), + AccountMeta::new(config.governed_account, false), + AccountMeta::new_readonly(*governed_token_owner, true), + AccountMeta::new_readonly(*payer, true), + AccountMeta::new_readonly(spl_token::id(), false), + AccountMeta::new_readonly(system_program::id(), false), + AccountMeta::new_readonly(sysvar::rent::id(), false), + ]; + + let instruction = GovernanceInstruction::CreateTokenGovernance { + config, + transfer_token_owner, + }; + + Instruction { + program_id: *program_id, + accounts, + data: instruction.try_to_vec().unwrap(), + } +} + /// Creates CreateProposal instruction #[allow(clippy::too_many_arguments)] pub fn create_proposal( @@ -849,7 +907,7 @@ pub fn insert_instruction( payer: &Pubkey, // Args index: u16, - hold_up_time: u64, + hold_up_time: u32, instruction: InstructionData, ) -> Instruction { let proposal_instruction_address = diff --git a/governance/program/src/processor/mod.rs b/governance/program/src/processor/mod.rs index 1a5218ae441..5efcaa35de8 100644 --- a/governance/program/src/processor/mod.rs +++ b/governance/program/src/processor/mod.rs @@ -8,6 +8,7 @@ mod process_create_mint_governance; mod process_create_program_governance; mod process_create_proposal; mod process_create_realm; +mod process_create_token_governance; mod process_deposit_governing_tokens; mod process_execute_instruction; mod process_finalize_vote; @@ -30,6 +31,7 @@ use process_create_mint_governance::*; use process_create_program_governance::*; use process_create_proposal::*; use process_create_realm::*; +use process_create_token_governance::*; use process_deposit_governing_tokens::*; use process_execute_instruction::*; use process_finalize_vote::*; @@ -103,6 +105,11 @@ pub fn process_instruction( transfer_mint_authority, } => process_create_mint_governance(program_id, accounts, config, transfer_mint_authority), + GovernanceInstruction::CreateTokenGovernance { + config, + transfer_token_owner, + } => process_create_token_governance(program_id, accounts, config, transfer_token_owner), + GovernanceInstruction::CreateAccountGovernance { config } => { process_create_account_governance(program_id, accounts, config) } diff --git a/governance/program/src/processor/process_cancel_proposal.rs b/governance/program/src/processor/process_cancel_proposal.rs index b6cb3fe0696..f4aca9edac0 100644 --- a/governance/program/src/processor/process_cancel_proposal.rs +++ b/governance/program/src/processor/process_cancel_proposal.rs @@ -37,7 +37,7 @@ pub fn process_cancel_proposal(program_id: &Pubkey, accounts: &[AccountInfo]) -> token_owner_record_data.assert_token_owner_or_delegate_is_signer(governance_authority_info)?; proposal_data.state = ProposalState::Cancelled; - proposal_data.closed_at = Some(clock.slot); + proposal_data.closed_at = Some(clock.unix_timestamp); proposal_data.serialize(&mut *proposal_info.data.borrow_mut())?; diff --git a/governance/program/src/processor/process_cast_vote.rs b/governance/program/src/processor/process_cast_vote.rs index bb0931eb873..7d92a9f7f81 100644 --- a/governance/program/src/processor/process_cast_vote.rs +++ b/governance/program/src/processor/process_cast_vote.rs @@ -61,7 +61,7 @@ pub fn process_cast_vote( governance_info.key, governing_token_mint_info.key, )?; - proposal_data.assert_can_cast_vote(&governance_data.config, clock.slot)?; + proposal_data.assert_can_cast_vote(&governance_data.config, clock.unix_timestamp)?; let mut token_owner_record_data = get_token_owner_record_data_for_realm_and_governing_mint( program_id, @@ -105,7 +105,11 @@ pub fn process_cast_vote( }; let governing_token_supply = get_spl_token_mint_supply(governing_token_mint_info)?; - proposal_data.try_tip_vote(governing_token_supply, &governance_data.config, clock.slot); + proposal_data.try_tip_vote( + governing_token_supply, + &governance_data.config, + clock.unix_timestamp, + ); proposal_data.serialize(&mut *proposal_info.data.borrow_mut())?; diff --git a/governance/program/src/processor/process_create_proposal.rs b/governance/program/src/processor/process_create_proposal.rs index 17822c44855..9fdcb67b984 100644 --- a/governance/program/src/processor/process_create_proposal.rs +++ b/governance/program/src/processor/process_create_proposal.rs @@ -81,7 +81,7 @@ pub fn process_create_proposal( name, description_link, - draft_at: clock.slot, + draft_at: clock.unix_timestamp, signing_off_at: None, voting_at: None, voting_completed_at: None, diff --git a/governance/program/src/processor/process_create_token_governance.rs b/governance/program/src/processor/process_create_token_governance.rs new file mode 100644 index 00000000000..b79e7aa4e0b --- /dev/null +++ b/governance/program/src/processor/process_create_token_governance.rs @@ -0,0 +1,77 @@ +//! Program state processor + +use crate::{ + state::{ + enums::GovernanceAccountType, + governance::{ + assert_is_valid_governance_config, get_token_governance_address_seeds, Governance, + GovernanceConfig, + }, + }, + tools::{ + account::create_and_serialize_account_signed, + spl_token::{assert_spl_token_owner_is_signer, set_spl_token_owner}, + }, +}; +use solana_program::{ + account_info::{next_account_info, AccountInfo}, + entrypoint::ProgramResult, + pubkey::Pubkey, + rent::Rent, + sysvar::Sysvar, +}; + +/// Processes CreateTokenGovernance instruction +pub fn process_create_token_governance( + program_id: &Pubkey, + accounts: &[AccountInfo], + config: GovernanceConfig, + transfer_token_owner: bool, +) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + + let realm_info = next_account_info(account_info_iter)?; // 0 + let token_governance_info = next_account_info(account_info_iter)?; // 1 + + let governed_token_info = next_account_info(account_info_iter)?; // 2 + let governed_token_owner_info = next_account_info(account_info_iter)?; // 3 + + let payer_info = next_account_info(account_info_iter)?; // 4 + let spl_token_info = next_account_info(account_info_iter)?; // 5 + + let system_info = next_account_info(account_info_iter)?; // 6 + + let rent_sysvar_info = next_account_info(account_info_iter)?; // 7 + let rent = &Rent::from_account_info(rent_sysvar_info)?; + + assert_is_valid_governance_config(program_id, &config, realm_info)?; + + let token_governance_data = Governance { + account_type: GovernanceAccountType::TokenGovernance, + config: config.clone(), + proposals_count: 0, + }; + + create_and_serialize_account_signed::( + payer_info, + token_governance_info, + &token_governance_data, + &get_token_governance_address_seeds(&config.realm, &config.governed_account), + program_id, + system_info, + rent, + )?; + + if transfer_token_owner { + set_spl_token_owner( + governed_token_info, + governed_token_owner_info, + token_governance_info.key, + spl_token_info, + )?; + } else { + assert_spl_token_owner_is_signer(governed_token_info, governed_token_owner_info)?; + } + + Ok(()) +} diff --git a/governance/program/src/processor/process_execute_instruction.rs b/governance/program/src/processor/process_execute_instruction.rs index d0f312684ce..1d8938c8130 100644 --- a/governance/program/src/processor/process_execute_instruction.rs +++ b/governance/program/src/processor/process_execute_instruction.rs @@ -39,7 +39,8 @@ pub fn process_execute_instruction(program_id: &Pubkey, accounts: &[AccountInfo] proposal_info.key, )?; - proposal_data.assert_can_execute_instruction(&proposal_instruction_data, clock.slot)?; + proposal_data + .assert_can_execute_instruction(&proposal_instruction_data, clock.unix_timestamp)?; // Execute instruction with Governance PDA as signer let instruction = Instruction::from(&proposal_instruction_data.instruction); @@ -59,7 +60,7 @@ pub fn process_execute_instruction(program_id: &Pubkey, accounts: &[AccountInfo] // Update proposal and instruction accounts if proposal_data.state == ProposalState::Succeeded { - proposal_data.executing_at = Some(clock.slot); + proposal_data.executing_at = Some(clock.unix_timestamp); proposal_data.state = ProposalState::Executing; } @@ -71,13 +72,13 @@ pub fn process_execute_instruction(program_id: &Pubkey, accounts: &[AccountInfo] if proposal_data.state == ProposalState::Executing && proposal_data.instructions_executed_count == proposal_data.instructions_count { - proposal_data.closed_at = Some(clock.slot); + proposal_data.closed_at = Some(clock.unix_timestamp); proposal_data.state = ProposalState::Completed; } proposal_data.serialize(&mut *proposal_info.data.borrow_mut())?; - proposal_instruction_data.executed_at = Some(clock.slot); + proposal_instruction_data.executed_at = Some(clock.unix_timestamp); proposal_instruction_data.serialize(&mut *proposal_instruction_info.data.borrow_mut())?; Ok(()) diff --git a/governance/program/src/processor/process_finalize_vote.rs b/governance/program/src/processor/process_finalize_vote.rs index 35172614509..235cf131f3f 100644 --- a/governance/program/src/processor/process_finalize_vote.rs +++ b/governance/program/src/processor/process_finalize_vote.rs @@ -41,7 +41,11 @@ pub fn process_finalize_vote(program_id: &Pubkey, accounts: &[AccountInfo]) -> P let governing_token_supply = get_spl_token_mint_supply(governing_token_mint_info)?; - proposal_data.finalize_vote(governing_token_supply, &governance_data.config, clock.slot)?; + proposal_data.finalize_vote( + governing_token_supply, + &governance_data.config, + clock.unix_timestamp, + )?; proposal_data.serialize(&mut *proposal_info.data.borrow_mut())?; diff --git a/governance/program/src/processor/process_insert_instruction.rs b/governance/program/src/processor/process_insert_instruction.rs index b352a7fafa3..8cbdcc0ff86 100644 --- a/governance/program/src/processor/process_insert_instruction.rs +++ b/governance/program/src/processor/process_insert_instruction.rs @@ -30,7 +30,7 @@ pub fn process_insert_instruction( program_id: &Pubkey, accounts: &[AccountInfo], index: u16, - hold_up_time: u64, + hold_up_time: u32, instruction: InstructionData, ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); @@ -73,7 +73,7 @@ pub fn process_insert_instruction( match index.cmp(&proposal_data.instructions_next_index) { Ordering::Greater => return Err(GovernanceError::InvalidInstructionIndex.into()), // If the index is the same as instructions_next_index then we are adding a new instruction - // If the index is below instructions_next_index then we are inserting into an existing empty slot + // If the index is below instructions_next_index then we are inserting into an existing empty space Ordering::Equal => { proposal_data.instructions_next_index = proposal_data .instructions_next_index diff --git a/governance/program/src/processor/process_sign_off_proposal.rs b/governance/program/src/processor/process_sign_off_proposal.rs index c4d809b503c..635af15cddb 100644 --- a/governance/program/src/processor/process_sign_off_proposal.rs +++ b/governance/program/src/processor/process_sign_off_proposal.rs @@ -41,7 +41,7 @@ pub fn process_sign_off_proposal(program_id: &Pubkey, accounts: &[AccountInfo]) signatory_record_data.serialize(&mut *signatory_record_info.data.borrow_mut())?; if proposal_data.signatories_signed_off_count == 0 { - proposal_data.signing_off_at = Some(clock.slot); + proposal_data.signing_off_at = Some(clock.unix_timestamp); proposal_data.state = ProposalState::SigningOff; } @@ -52,7 +52,7 @@ pub fn process_sign_off_proposal(program_id: &Pubkey, accounts: &[AccountInfo]) // If all Signatories signed off we can start voting if proposal_data.signatories_signed_off_count == proposal_data.signatories_count { - proposal_data.voting_at = Some(clock.slot); + proposal_data.voting_at = Some(clock.unix_timestamp); proposal_data.state = ProposalState::Voting; } diff --git a/governance/program/src/state/enums.rs b/governance/program/src/state/enums.rs index 3ec2360a654..1d8eaef146f 100644 --- a/governance/program/src/state/enums.rs +++ b/governance/program/src/state/enums.rs @@ -35,6 +35,9 @@ pub enum GovernanceAccountType { /// Mint Governance account MintGovernance, + + /// Token Governance account + TokenGovernance, } impl Default for GovernanceAccountType { diff --git a/governance/program/src/state/governance.rs b/governance/program/src/state/governance.rs index 93e6114d2a2..34ca08d33ad 100644 --- a/governance/program/src/state/governance.rs +++ b/governance/program/src/state/governance.rs @@ -31,11 +31,11 @@ pub struct GovernanceConfig { /// Minimum number of tokens a governance token owner must possess to be able to create a proposal pub min_tokens_to_create_proposal: u16, - /// Minimum waiting time in slots for an instruction to be executed after proposal is voted on - pub min_instruction_hold_up_time: u64, + /// Minimum waiting time in seconds for an instruction to be executed after proposal is voted on + pub min_instruction_hold_up_time: u32, - /// Time limit in slots for proposal to be open for voting - pub max_voting_time: u64, + /// Time limit in seconds for proposal to be open for voting + pub max_voting_time: u32, } /// Governance Account @@ -59,6 +59,7 @@ impl IsInitialized for Governance { self.account_type == GovernanceAccountType::AccountGovernance || self.account_type == GovernanceAccountType::ProgramGovernance || self.account_type == GovernanceAccountType::MintGovernance + || self.account_type == GovernanceAccountType::TokenGovernance } } @@ -77,6 +78,10 @@ impl Governance { GovernanceAccountType::MintGovernance => { get_mint_governance_address_seeds(&self.config.realm, &self.config.governed_account) } + GovernanceAccountType::TokenGovernance => get_token_governance_address_seeds( + &self.config.realm, + &self.config.governed_account, + ), _ => return Err(GovernanceError::InvalidAccountType.into()), }; @@ -142,6 +147,29 @@ pub fn get_mint_governance_address<'a>( .0 } +/// Returns TokenGovernance PDA seeds +pub fn get_token_governance_address_seeds<'a>( + realm: &'a Pubkey, + governed_token: &'a Pubkey, +) -> [&'a [u8]; 3] { + // 'token-governance' prefix ensures uniqueness of the PDA + // Note: Only the current token account owner can create an account with this PDA using CreateTokenGovernance instruction + [b"token-governance", realm.as_ref(), governed_token.as_ref()] +} + +/// Returns TokenGovernance PDA address +pub fn get_token_governance_address<'a>( + program_id: &Pubkey, + realm: &'a Pubkey, + governed_token: &'a Pubkey, +) -> Pubkey { + Pubkey::find_program_address( + &get_token_governance_address_seeds(realm, governed_token), + program_id, + ) + .0 +} + /// Returns AccountGovernance PDA seeds pub fn get_account_governance_address_seeds<'a>( realm: &'a Pubkey, diff --git a/governance/program/src/state/proposal.rs b/governance/program/src/state/proposal.rs index 095706cad30..2162083ccc9 100644 --- a/governance/program/src/state/proposal.rs +++ b/governance/program/src/state/proposal.rs @@ -1,8 +1,9 @@ //! Proposal Account +use solana_program::clock::UnixTimestamp; use solana_program::{ - account_info::AccountInfo, epoch_schedule::Slot, program_error::ProgramError, - program_pack::IsInitialized, pubkey::Pubkey, + account_info::AccountInfo, program_error::ProgramError, program_pack::IsInitialized, + pubkey::Pubkey, }; use crate::tools::account::get_account_data; @@ -54,22 +55,22 @@ pub struct Proposal { pub no_votes_count: u64, /// When the Proposal was created and entered Draft state - pub draft_at: Slot, + pub draft_at: UnixTimestamp, /// When Signatories started signing off the Proposal - pub signing_off_at: Option, + pub signing_off_at: Option, /// When the Proposal began voting - pub voting_at: Option, + pub voting_at: Option, /// When the Proposal ended voting and entered either Succeeded or Defeated - pub voting_completed_at: Option, + pub voting_completed_at: Option, /// When the Proposal entered Executing state - pub executing_at: Option, + pub executing_at: Option, /// When the Proposal entered final state Completed or Cancelled and was closed - pub closed_at: Option, + pub closed_at: Option, /// The number of the instructions already executed pub instructions_executed_count: u16, @@ -135,7 +136,7 @@ impl Proposal { pub fn assert_can_cast_vote( &self, config: &GovernanceConfig, - current_slot: Slot, + current_unix_timestamp: UnixTimestamp, ) -> Result<(), ProgramError> { self.assert_is_voting_state() .map_err(|_| GovernanceError::InvalidStateCannotVote)?; @@ -144,9 +145,9 @@ impl Proposal { if self .voting_at .unwrap() - .checked_add(config.max_voting_time) + .checked_add(config.max_voting_time as i64) .unwrap() - < current_slot + < current_unix_timestamp { return Err(GovernanceError::ProposalVotingTimeExpired.into()); } @@ -158,7 +159,7 @@ impl Proposal { pub fn assert_can_finalize_vote( &self, config: &GovernanceConfig, - current_slot: Slot, + current_unix_timestamp: UnixTimestamp, ) -> Result<(), ProgramError> { self.assert_is_voting_state() .map_err(|_| GovernanceError::InvalidStateCannotFinalize)?; @@ -167,9 +168,9 @@ impl Proposal { if self .voting_at .unwrap() - .checked_add(config.max_voting_time) + .checked_add(config.max_voting_time as i64) .unwrap() - >= current_slot + >= current_unix_timestamp { return Err(GovernanceError::CannotFinalizeVotingInProgress.into()); } @@ -183,12 +184,12 @@ impl Proposal { &mut self, governing_token_supply: u64, config: &GovernanceConfig, - current_slot: Slot, + current_unix_timestamp: UnixTimestamp, ) -> Result<(), ProgramError> { - self.assert_can_finalize_vote(config, current_slot)?; + self.assert_can_finalize_vote(config, current_unix_timestamp)?; self.state = self.get_final_vote_state(governing_token_supply, config); - self.voting_completed_at = Some(current_slot); + self.voting_completed_at = Some(current_unix_timestamp); Ok(()) } @@ -219,11 +220,11 @@ impl Proposal { &mut self, governing_token_supply: u64, config: &GovernanceConfig, - current_slot: Slot, + current_unix_timestamp: UnixTimestamp, ) { if let Some(tipped_state) = self.try_get_tipped_vote_state(governing_token_supply, config) { self.state = tipped_state; - self.voting_completed_at = Some(current_slot); + self.voting_completed_at = Some(current_unix_timestamp); } } @@ -282,7 +283,7 @@ impl Proposal { pub fn assert_can_execute_instruction( &self, proposal_instruction_data: &ProposalInstruction, - current_slot: Slot, + current_unix_timestamp: UnixTimestamp, ) -> Result<(), ProgramError> { match self.state { ProposalState::Succeeded | ProposalState::Executing => {} @@ -299,9 +300,9 @@ impl Proposal { if self .voting_completed_at .unwrap() - .checked_add(proposal_instruction_data.hold_up_time) + .checked_add(proposal_instruction_data.hold_up_time as i64) .unwrap() - >= current_slot + >= current_unix_timestamp { return Err(GovernanceError::CannotExecuteInstructionWithinHoldUpTime.into()); } @@ -813,16 +814,16 @@ mod test { let mut governance_config = create_test_governance_config(); governance_config.yes_vote_threshold_percentage = test_case.vote_threshold_percentage; - let current_slot = 15_u64; + let current_timestamp = 15_i64; // Act - proposal.try_tip_vote(test_case.governing_token_supply, &governance_config,current_slot); + proposal.try_tip_vote(test_case.governing_token_supply, &governance_config,current_timestamp); // Assert assert_eq!(proposal.state,test_case.expected_tipped_state,"CASE: {:?}",test_case); if test_case.expected_tipped_state != ProposalState::Voting { - assert_eq!(Some(current_slot),proposal.voting_completed_at) + assert_eq!(Some(current_timestamp),proposal.voting_completed_at) } } @@ -837,14 +838,14 @@ mod test { let mut governance_config = create_test_governance_config(); governance_config.yes_vote_threshold_percentage = test_case.vote_threshold_percentage; - let current_slot = 16_u64; + let current_timestamp = 16_i64; // Act - proposal.finalize_vote(test_case.governing_token_supply, &governance_config,current_slot).unwrap(); + proposal.finalize_vote(test_case.governing_token_supply, &governance_config,current_timestamp).unwrap(); // Assert assert_eq!(proposal.state,test_case.expected_finalized_state,"CASE: {:?}",test_case); - assert_eq!(Some(current_slot),proposal.voting_completed_at); + assert_eq!(Some(current_timestamp),proposal.voting_completed_at); } } @@ -879,11 +880,10 @@ mod test { let mut governance_config = create_test_governance_config(); governance_config.yes_vote_threshold_percentage = yes_vote_threshold_percentage; - let current_slot = 15_u64; - + let current_timestamp = 15_i64; // Act - proposal.try_tip_vote(governing_token_supply, &governance_config,current_slot); + proposal.try_tip_vote(governing_token_supply, &governance_config,current_timestamp); // Assert let yes_vote_threshold_count = get_vote_threshold_count(yes_vote_threshold_percentage,governing_token_supply); @@ -916,10 +916,10 @@ mod test { let mut governance_config = create_test_governance_config(); governance_config.yes_vote_threshold_percentage = yes_vote_threshold_percentage; - let current_slot = 16_u64; + let current_timestamp = 16_i64; // Act - proposal.finalize_vote(governing_token_supply, &governance_config,current_slot).unwrap(); + proposal.finalize_vote(governing_token_supply, &governance_config,current_timestamp).unwrap(); // Assert let yes_vote_threshold_count = get_vote_threshold_count(yes_vote_threshold_percentage,governing_token_supply); @@ -940,11 +940,12 @@ mod test { proposal.state = ProposalState::Voting; let governance_config = create_test_governance_config(); - let current_slot = proposal.voting_at.unwrap() + governance_config.max_voting_time; + let current_timestamp = + proposal.voting_at.unwrap() + governance_config.max_voting_time as i64; // Act let err = proposal - .finalize_vote(100, &governance_config, current_slot) + .finalize_vote(100, &governance_config, current_timestamp) .err() .unwrap(); @@ -959,10 +960,11 @@ mod test { proposal.state = ProposalState::Voting; let governance_config = create_test_governance_config(); - let current_slot = proposal.voting_at.unwrap() + governance_config.max_voting_time + 1; + let current_timestamp = + proposal.voting_at.unwrap() + governance_config.max_voting_time as i64 + 1; // Act - let result = proposal.finalize_vote(100, &governance_config, current_slot); + let result = proposal.finalize_vote(100, &governance_config, current_timestamp); // Assert assert_eq!(result, Ok(())); @@ -975,11 +977,12 @@ mod test { proposal.state = ProposalState::Voting; let governance_config = create_test_governance_config(); - let current_slot = proposal.voting_at.unwrap() + governance_config.max_voting_time + 1; + let current_timestamp = + proposal.voting_at.unwrap() + governance_config.max_voting_time as i64 + 1; // Act let err = proposal - .assert_can_cast_vote(&governance_config, current_slot) + .assert_can_cast_vote(&governance_config, current_timestamp) .err() .unwrap(); @@ -994,10 +997,11 @@ mod test { proposal.state = ProposalState::Voting; let governance_config = create_test_governance_config(); - let current_slot = proposal.voting_at.unwrap() + governance_config.max_voting_time; + let current_timestamp = + proposal.voting_at.unwrap() + governance_config.max_voting_time as i64; // Act - let result = proposal.assert_can_cast_vote(&governance_config, current_slot); + let result = proposal.assert_can_cast_vote(&governance_config, current_timestamp); // Assert assert_eq!(result, Ok(())); diff --git a/governance/program/src/state/proposal_instruction.rs b/governance/program/src/state/proposal_instruction.rs index c4ad140d953..6144cef2ad6 100644 --- a/governance/program/src/state/proposal_instruction.rs +++ b/governance/program/src/state/proposal_instruction.rs @@ -9,7 +9,7 @@ use crate::{ use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use solana_program::{ account_info::AccountInfo, - epoch_schedule::Slot, + clock::UnixTimestamp, instruction::{AccountMeta, Instruction}, program_error::ProgramError, program_pack::IsInitialized, @@ -86,8 +86,8 @@ pub struct ProposalInstruction { /// The Proposal the instruction belongs to pub proposal: Pubkey, - /// Minimum waiting time in slots for the instruction to be executed once proposal is voted on - pub hold_up_time: u64, + /// Minimum waiting time in seconds for the instruction to be executed once proposal is voted on + pub hold_up_time: u32, /// Instruction to execute /// The instruction will be signed by Governance PDA the Proposal belongs to @@ -95,12 +95,12 @@ pub struct ProposalInstruction { pub instruction: InstructionData, /// Executed at flag - pub executed_at: Option, + pub executed_at: Option, } impl AccountMaxSize for ProposalInstruction { fn get_max_size(&self) -> Option { - Some(self.instruction.accounts.len() * 34 + self.instruction.data.len() + 90) + Some(self.instruction.accounts.len() * 34 + self.instruction.data.len() + 86) } } diff --git a/governance/program/src/tools/spl_token.rs b/governance/program/src/tools/spl_token.rs index 69a4652edc8..86eacc9e051 100644 --- a/governance/program/src/tools/spl_token.rs +++ b/governance/program/src/tools/spl_token.rs @@ -325,3 +325,49 @@ pub fn set_spl_token_mint_authority<'a>( Ok(()) } + +/// Asserts current token owner matches the given owner and it's signer of the transaction +pub fn assert_spl_token_owner_is_signer( + token_info: &AccountInfo, + token_owner_info: &AccountInfo, +) -> Result<(), ProgramError> { + let token_owner = get_spl_token_owner(token_info)?; + + if token_owner != *token_owner_info.key { + return Err(GovernanceError::InvalidTokenOwner.into()); + } + + if !token_owner_info.is_signer { + return Err(GovernanceError::TokenOwnerMustSign.into()); + } + + Ok(()) +} + +/// Sets new token account owner +pub fn set_spl_token_owner<'a>( + token_info: &AccountInfo<'a>, + token_owner: &AccountInfo<'a>, + new_token_owner: &Pubkey, + spl_token_info: &AccountInfo<'a>, +) -> Result<(), ProgramError> { + let set_authority_ix = set_authority( + &spl_token::id(), + token_info.key, + Some(new_token_owner), + spl_token::instruction::AuthorityType::AccountOwner, + token_owner.key, + &[], + )?; + + invoke( + &set_authority_ix, + &[ + token_info.clone(), + token_owner.clone(), + spl_token_info.clone(), + ], + )?; + + Ok(()) +} diff --git a/governance/program/tests/process_cancel_proposal.rs b/governance/program/tests/process_cancel_proposal.rs index 70f407b3863..ad4cf610b68 100644 --- a/governance/program/tests/process_cancel_proposal.rs +++ b/governance/program/tests/process_cancel_proposal.rs @@ -29,6 +29,8 @@ async fn test_cancel_proposal() { .await .unwrap(); + let clock = governance_test.get_clock().await; + // Act governance_test .cancel_proposal(&proposal_cookie, &token_owner_record_cookie) @@ -41,7 +43,7 @@ async fn test_cancel_proposal() { .await; assert_eq!(ProposalState::Cancelled, proposal_account.state); - assert_eq!(Some(1), proposal_account.closed_at); + assert_eq!(Some(clock.unix_timestamp), proposal_account.closed_at); } #[tokio::test] diff --git a/governance/program/tests/process_cast_vote.rs b/governance/program/tests/process_cast_vote.rs index 95b1b3242f9..dec6f5c34ae 100644 --- a/governance/program/tests/process_cast_vote.rs +++ b/governance/program/tests/process_cast_vote.rs @@ -29,6 +29,8 @@ async fn test_cast_vote() { .await .unwrap(); + let clock = governance_test.get_clock().await; + // Act let vote_record_cookie = governance_test .with_cast_vote(&proposal_cookie, &token_owner_record_cookie, Vote::Yes) @@ -54,7 +56,10 @@ async fn test_cast_vote() { ); assert_eq!(proposal_account.state, ProposalState::Succeeded); - assert_eq!(proposal_account.voting_completed_at, Some(1)); + assert_eq!( + proposal_account.voting_completed_at, + Some(clock.unix_timestamp) + ); let token_owner_record = governance_test .get_token_owner_record_account(&token_owner_record_cookie.address) @@ -501,13 +506,12 @@ async fn test_cast_vote_with_voting_time_expired_error() { .get_proposal_account(&proposal_cookie.address) .await; - let vote_expired_at_slot = account_governance_cookie.account.config.max_voting_time - + proposal_account.voting_at.unwrap() - + 1; + let vote_expired_at = proposal_account.voting_at.unwrap() + + account_governance_cookie.account.config.max_voting_time as i64; + governance_test - .context - .warp_to_slot(vote_expired_at_slot) - .unwrap(); + .advance_clock_past_timestamp(vote_expired_at) + .await; // Act @@ -553,7 +557,7 @@ async fn test_cast_vote_with_cast_twice_error() { .await .unwrap(); - governance_test.context.warp_to_slot(5).unwrap(); + governance_test.advance_clock().await; // Act let err = governance_test diff --git a/governance/program/tests/process_create_proposal.rs b/governance/program/tests/process_create_proposal.rs index cb3efc7914b..9fe239c8b08 100644 --- a/governance/program/tests/process_create_proposal.rs +++ b/governance/program/tests/process_create_proposal.rs @@ -44,7 +44,6 @@ async fn test_community_proposal_created() { .await; assert_eq!(1, account_governance_account.proposals_count); - assert_eq!(proposal_account.draft_at, 1); } #[tokio::test] diff --git a/governance/program/tests/process_create_token_governance.rs b/governance/program/tests/process_create_token_governance.rs new file mode 100644 index 00000000000..c86ba0dfedc --- /dev/null +++ b/governance/program/tests/process_create_token_governance.rs @@ -0,0 +1,170 @@ +#![cfg(feature = "test-bpf")] +mod program_test; + +use solana_program_test::*; + +use program_test::*; +use solana_sdk::{signature::Keypair, signer::Signer}; +use spl_governance::error::GovernanceError; +use spl_token::error::TokenError; + +#[tokio::test] +async fn test_create_token_governance() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let realm_cookie = governance_test.with_realm().await; + let governed_token_cookie = governance_test.with_governed_token().await; + + // Act + let token_governance_cookie = governance_test + .with_token_governance(&realm_cookie, &governed_token_cookie) + .await + .unwrap(); + + // Assert + let token_governance_account = governance_test + .get_governance_account(&token_governance_cookie.address) + .await; + + assert_eq!(token_governance_cookie.account, token_governance_account); + + let token_account = governance_test + .get_token_account(&governed_token_cookie.address) + .await; + + assert_eq!(token_governance_cookie.address, token_account.owner); +} + +#[tokio::test] +async fn test_create_token_governance_without_transferring_token_owner() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let realm_cookie = governance_test.with_realm().await; + let mut governed_token_cookie = governance_test.with_governed_token().await; + + governed_token_cookie.transfer_token_owner = false; + + // Act + let token_governance_cookie = governance_test + .with_token_governance(&realm_cookie, &governed_token_cookie) + .await + .unwrap(); + + // Assert + let token_governance_account = governance_test + .get_governance_account(&token_governance_cookie.address) + .await; + + assert_eq!(token_governance_cookie.account, token_governance_account); + + let token_account = governance_test + .get_token_account(&governed_token_cookie.address) + .await; + + assert_eq!( + governed_token_cookie.token_owner.pubkey(), + token_account.owner + ); +} + +#[tokio::test] +async fn test_create_token_governance_without_transferring_token_owner_with_invalid_token_owner_error( +) { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let realm_cookie = governance_test.with_realm().await; + let mut governed_token_cookie = governance_test.with_governed_token().await; + + governed_token_cookie.transfer_token_owner = false; + governed_token_cookie.token_owner = Keypair::new(); + + // Act + let err = governance_test + .with_token_governance(&realm_cookie, &governed_token_cookie) + .await + .err() + .unwrap(); + + // Assert + assert_eq!(err, GovernanceError::InvalidTokenOwner.into()); +} + +#[tokio::test] +async fn test_create_token_governance_without_transferring_token_owner_with_owner_not_signed_error() +{ + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let realm_cookie = governance_test.with_realm().await; + let mut governed_token_cookie = governance_test.with_governed_token().await; + + governed_token_cookie.transfer_token_owner = false; + + // Act + let err = governance_test + .with_token_governance_using_instruction( + &realm_cookie, + &governed_token_cookie, + |i| { + i.accounts[3].is_signer = false; // governed_token_owner + }, + Some(&[]), + ) + .await + .err() + .unwrap(); + + // Assert + assert_eq!(err, GovernanceError::TokenOwnerMustSign.into()); +} + +#[tokio::test] +async fn test_create_token_governance_with_invalid_token_owner_error() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let realm_cookie = governance_test.with_realm().await; + let mut governed_token_cookie = governance_test.with_governed_token().await; + + governed_token_cookie.token_owner = Keypair::new(); + + // Act + let err = governance_test + .with_token_governance(&realm_cookie, &governed_token_cookie) + .await + .err() + .unwrap(); + + // Assert + assert_eq!(err, TokenError::OwnerMismatch.into()); +} + +#[tokio::test] +async fn test_create_token_governance_with_invalid_realm_error() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let mut realm_cookie = governance_test.with_realm().await; + let governed_token_cookie = governance_test.with_governed_token().await; + + let token_governance_cookie = governance_test + .with_token_governance(&realm_cookie, &governed_token_cookie) + .await + .unwrap(); + + // try to use Governance account other than Realm as realm + realm_cookie.address = token_governance_cookie.address; + + // Act + let err = governance_test + .with_token_governance(&realm_cookie, &governed_token_cookie) + .await + .err() + .unwrap(); + + // Assert + assert_eq!(err, GovernanceError::InvalidAccountType.into()); +} diff --git a/governance/program/tests/process_deposit_governing_tokens.rs b/governance/program/tests/process_deposit_governing_tokens.rs index c07f450938b..3084c557f34 100644 --- a/governance/program/tests/process_deposit_governing_tokens.rs +++ b/governance/program/tests/process_deposit_governing_tokens.rs @@ -108,7 +108,7 @@ async fn test_deposit_subsequent_community_tokens() { .governing_token_deposit_amount + deposit_amount; - governance_test.context.warp_to_slot(5).unwrap(); + governance_test.advance_clock().await; // Act governance_test @@ -154,7 +154,7 @@ async fn test_deposit_subsequent_council_tokens() { .governing_token_deposit_amount + deposit_amount; - governance_test.context.warp_to_slot(5).unwrap(); + governance_test.advance_clock().await; // Act governance_test diff --git a/governance/program/tests/process_execute_instruction.rs b/governance/program/tests/process_execute_instruction.rs index ccf3412d21e..13171dbcca3 100644 --- a/governance/program/tests/process_execute_instruction.rs +++ b/governance/program/tests/process_execute_instruction.rs @@ -59,14 +59,100 @@ async fn test_execute_mint_instruction() { .await .unwrap(); - // Advance slot past hold_up_time - let execute_at_slot = 1 + proposal_instruction_cookie.account.hold_up_time + 1; + // Advance timestamp past hold_up_time + governance_test + .advance_clock_by_min_timespan(proposal_instruction_cookie.account.hold_up_time as u64) + .await; + + let clock = governance_test.get_clock().await; + // Act governance_test - .context - .warp_to_slot(execute_at_slot) + .execute_instruction(&proposal_cookie, &proposal_instruction_cookie) + .await + .unwrap(); + + // Assert + + let proposal_account = governance_test + .get_proposal_account(&proposal_cookie.address) + .await; + + assert_eq!(1, proposal_account.instructions_executed_count); + assert_eq!(ProposalState::Completed, proposal_account.state); + assert_eq!(Some(clock.unix_timestamp), proposal_account.closed_at); + assert_eq!(Some(clock.unix_timestamp), proposal_account.executing_at); + + let proposal_instruction_account = governance_test + .get_proposal_instruction_account(&proposal_instruction_cookie.address) + .await; + + assert_eq!( + Some(clock.unix_timestamp), + proposal_instruction_account.executed_at + ); + + let instruction_token_account = governance_test + .get_token_account(&proposal_instruction_cookie.account.instruction.accounts[1].pubkey) + .await; + + assert_eq!(10, instruction_token_account.amount); +} + +#[tokio::test] +async fn test_execute_transfer_instruction() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let realm_cookie = governance_test.with_realm().await; + let governed_token_cookie = governance_test.with_governed_token().await; + + let mut token_governance_cookie = governance_test + .with_token_governance(&realm_cookie, &governed_token_cookie) + .await + .unwrap(); + + let token_owner_record_cookie = governance_test + .with_community_token_deposit(&realm_cookie) + .await; + + let mut proposal_cookie = governance_test + .with_proposal(&token_owner_record_cookie, &mut token_governance_cookie) + .await .unwrap(); + let signatory_record_cookie = governance_test + .with_signatory(&proposal_cookie, &token_owner_record_cookie) + .await + .unwrap(); + + let proposal_instruction_cookie = governance_test + .with_transfer_tokens_instruction( + &governed_token_cookie, + &mut proposal_cookie, + &token_owner_record_cookie, + None, + ) + .await + .unwrap(); + + governance_test + .sign_off_proposal(&proposal_cookie, &signatory_record_cookie) + .await + .unwrap(); + + governance_test + .with_cast_vote(&proposal_cookie, &token_owner_record_cookie, Vote::Yes) + .await + .unwrap(); + + // Advance timestamp past hold_up_time + governance_test + .advance_clock_by_min_timespan(proposal_instruction_cookie.account.hold_up_time as u64) + .await; + + let clock = governance_test.get_clock().await; + // Act governance_test .execute_instruction(&proposal_cookie, &proposal_instruction_cookie) @@ -81,15 +167,15 @@ async fn test_execute_mint_instruction() { assert_eq!(1, proposal_account.instructions_executed_count); assert_eq!(ProposalState::Completed, proposal_account.state); - assert_eq!(Some(execute_at_slot), proposal_account.closed_at); - assert_eq!(Some(execute_at_slot), proposal_account.executing_at); + assert_eq!(Some(clock.unix_timestamp), proposal_account.closed_at); + assert_eq!(Some(clock.unix_timestamp), proposal_account.executing_at); let proposal_instruction_account = governance_test .get_proposal_instruction_account(&proposal_instruction_cookie.address) .await; assert_eq!( - Some(execute_at_slot), + Some(clock.unix_timestamp), proposal_instruction_account.executed_at ); @@ -97,7 +183,7 @@ async fn test_execute_mint_instruction() { .get_token_account(&proposal_instruction_cookie.account.instruction.accounts[1].pubkey) .await; - assert_eq!(10, instruction_token_account.amount); + assert_eq!(15, instruction_token_account.amount); } #[tokio::test] @@ -146,13 +232,10 @@ async fn test_execute_upgrade_program_instruction() { .await .unwrap(); - // Advance slot past hold_up_time - let execute_at_slot = 1 + proposal_instruction_cookie.account.hold_up_time + 1; - + // Advance timestamp past hold_up_time governance_test - .context - .warp_to_slot(execute_at_slot) - .unwrap(); + .advance_clock_by_min_timespan(proposal_instruction_cookie.account.hold_up_time as u64) + .await; // Ensure we can invoke the governed program before upgrade let governed_program_instruction = Instruction::new_with_bytes( @@ -174,6 +257,8 @@ async fn test_execute_upgrade_program_instruction() { // solana_bpf_rust_upgradable returns CustomError == 42 assert_eq!(ProgramError::Custom(42), err); + let clock = governance_test.get_clock().await; + // Act governance_test .execute_instruction(&proposal_cookie, &proposal_instruction_cookie) @@ -188,23 +273,21 @@ async fn test_execute_upgrade_program_instruction() { assert_eq!(1, proposal_account.instructions_executed_count); assert_eq!(ProposalState::Completed, proposal_account.state); - assert_eq!(Some(execute_at_slot), proposal_account.closed_at); - assert_eq!(Some(execute_at_slot), proposal_account.executing_at); + assert_eq!(Some(clock.unix_timestamp), proposal_account.closed_at); + assert_eq!(Some(clock.unix_timestamp), proposal_account.executing_at); let proposal_instruction_account = governance_test .get_proposal_instruction_account(&proposal_instruction_cookie.address) .await; assert_eq!( - Some(execute_at_slot), + Some(clock.unix_timestamp), proposal_instruction_account.executed_at ); // Assert we can invoke the governed program after upgrade - governance_test - .context - .warp_to_slot(execute_at_slot + 10) - .unwrap(); + + governance_test.advance_clock().await; let err = governance_test .process_transaction(&[governed_program_instruction.clone()], None) @@ -323,7 +406,7 @@ async fn test_execute_instruction_with_invalid_state_errors() { .await .unwrap(); - governance_test.context.warp_to_slot(5).unwrap(); + governance_test.advance_clock().await; // Act let err = governance_test @@ -339,13 +422,10 @@ async fn test_execute_instruction_with_invalid_state_errors() { ); // Arrange - // Advance slot past hold_up_time - let execute_at_slot = 1 + proposal_instruction_cookie.account.hold_up_time + 1; - + // Advance timestamp past hold_up_time governance_test - .context - .warp_to_slot(execute_at_slot) - .unwrap(); + .advance_clock_by_min_timespan(proposal_instruction_cookie.account.hold_up_time as u64) + .await; // Act governance_test @@ -363,10 +443,7 @@ async fn test_execute_instruction_with_invalid_state_errors() { // Arrange - governance_test - .context - .warp_to_slot(execute_at_slot + 10) - .unwrap(); + governance_test.advance_clock().await; // Act let err = governance_test @@ -429,13 +506,11 @@ async fn test_execute_instruction_for_other_proposal_error() { .await .unwrap(); - // Advance slot past hold_up_time - let execute_at_slot = 1 + proposal_instruction_cookie.account.hold_up_time + 1; + // Advance clock past hold_up_time governance_test - .context - .warp_to_slot(execute_at_slot) - .unwrap(); + .advance_clock_by_min_timespan(proposal_instruction_cookie.account.hold_up_time as u64) + .await; let proposal_cookie2 = governance_test .with_proposal(&token_owner_record_cookie, &mut mint_governance_cookie) @@ -508,23 +583,18 @@ async fn test_execute_mint_instruction_twice_error() { .await .unwrap(); - // Advance slot past hold_up_time - let execute_at_slot = 1 + proposal_instruction_cookie.account.hold_up_time + 1; + // Advance clock past hold_up_time governance_test - .context - .warp_to_slot(execute_at_slot) - .unwrap(); + .advance_clock_by_min_timespan(proposal_instruction_cookie.account.hold_up_time as u64) + .await; governance_test .execute_instruction(&proposal_cookie, &proposal_instruction_cookie) .await .unwrap(); - governance_test - .context - .warp_to_slot(execute_at_slot + 10) - .unwrap(); + governance_test.advance_clock().await; // Act diff --git a/governance/program/tests/process_finalize_vote.rs b/governance/program/tests/process_finalize_vote.rs index b5a50eaecb6..e606e795fe3 100644 --- a/governance/program/tests/process_finalize_vote.rs +++ b/governance/program/tests/process_finalize_vote.rs @@ -56,14 +56,15 @@ async fn test_finalize_vote_to_succeeded() { assert_eq!(ProposalState::Voting, proposal_account.state); - // Advance slot past max_voting_time - let vote_expired_at_slot = account_governance_cookie.account.config.max_voting_time - + proposal_account.voting_at.unwrap() - + 1; + // Advance timestamp past max_voting_time governance_test - .context - .warp_to_slot(vote_expired_at_slot) - .unwrap(); + .advance_clock_past_timestamp( + account_governance_cookie.account.config.max_voting_time as i64 + + proposal_account.voting_at.unwrap(), + ) + .await; + + let clock = governance_test.get_clock().await; // Act @@ -80,7 +81,7 @@ async fn test_finalize_vote_to_succeeded() { assert_eq!(proposal_account.state, ProposalState::Succeeded); assert_eq!( - Some(vote_expired_at_slot), + Some(clock.unix_timestamp), proposal_account.voting_completed_at ); } @@ -124,14 +125,13 @@ async fn test_finalize_vote_to_defeated() { assert_eq!(ProposalState::Voting, proposal_account.state); - // Advance slot past max_voting_time - let vote_expired_at_slot = account_governance_cookie.account.config.max_voting_time - + proposal_account.voting_at.unwrap() - + 1; + // Advance clock past max_voting_time governance_test - .context - .warp_to_slot(vote_expired_at_slot) - .unwrap(); + .advance_clock_past_timestamp( + account_governance_cookie.account.config.max_voting_time as i64 + + proposal_account.voting_at.unwrap(), + ) + .await; // Act diff --git a/governance/program/tests/process_sign_off_proposal.rs b/governance/program/tests/process_sign_off_proposal.rs index bdeab60a33d..90d409de8c3 100644 --- a/governance/program/tests/process_sign_off_proposal.rs +++ b/governance/program/tests/process_sign_off_proposal.rs @@ -34,6 +34,8 @@ async fn test_sign_off_proposal() { .await .unwrap(); + let clock = governance_test.get_clock().await; + // Act governance_test .sign_off_proposal(&proposal_cookie, &signatory_record_cookie) @@ -48,8 +50,8 @@ async fn test_sign_off_proposal() { assert_eq!(1, proposal_account.signatories_count); assert_eq!(1, proposal_account.signatories_signed_off_count); assert_eq!(ProposalState::Voting, proposal_account.state); - assert_eq!(Some(1), proposal_account.signing_off_at); - assert_eq!(Some(1), proposal_account.voting_at); + assert_eq!(Some(clock.unix_timestamp), proposal_account.signing_off_at); + assert_eq!(Some(clock.unix_timestamp), proposal_account.voting_at); let signatory_record_account = governance_test .get_signatory_record_account(&signatory_record_cookie.address) diff --git a/governance/program/tests/program_test/cookies.rs b/governance/program/tests/program_test/cookies.rs index 5729ca4a170..48230cb2597 100644 --- a/governance/program/tests/program_test/cookies.rs +++ b/governance/program/tests/program_test/cookies.rs @@ -68,6 +68,14 @@ pub struct GovernedMintCookie { pub transfer_mint_authority: bool, } +#[derive(Debug)] +pub struct GovernedTokenCookie { + pub address: Pubkey, + pub token_owner: Keypair, + pub transfer_token_owner: bool, + pub token_mint: Pubkey, +} + #[derive(Debug)] pub struct GovernedAccountCookie { pub address: Pubkey, diff --git a/governance/program/tests/program_test/mod.rs b/governance/program/tests/program_test/mod.rs index 8a6b47fe311..358e4006804 100644 --- a/governance/program/tests/program_test/mod.rs +++ b/governance/program/tests/program_test/mod.rs @@ -4,12 +4,13 @@ use borsh::BorshDeserialize; use solana_program::{ borsh::try_from_slice_unchecked, bpf_loader_upgradeable::{self, UpgradeableLoaderState}, + clock::{Clock, UnixTimestamp}, instruction::{AccountMeta, Instruction}, program_error::ProgramError, program_pack::{IsInitialized, Pack}, pubkey::Pubkey, rent::Rent, - system_instruction, + system_instruction, sysvar, }; use bincode::deserialize; @@ -26,16 +27,17 @@ use spl_governance::{ instruction::{ add_signatory, cancel_proposal, cast_vote, create_account_governance, create_mint_governance, create_program_governance, create_proposal, create_realm, - deposit_governing_tokens, execute_instruction, finalize_vote, insert_instruction, - relinquish_vote, remove_instruction, remove_signatory, set_governance_delegate, - sign_off_proposal, withdraw_governing_tokens, Vote, + create_token_governance, deposit_governing_tokens, execute_instruction, finalize_vote, + insert_instruction, relinquish_vote, remove_instruction, remove_signatory, + set_governance_delegate, sign_off_proposal, withdraw_governing_tokens, Vote, }, processor::process_instruction, state::{ enums::{GovernanceAccountType, ProposalState, VoteWeight}, governance::{ get_account_governance_address, get_mint_governance_address, - get_program_governance_address, Governance, GovernanceConfig, + get_program_governance_address, get_token_governance_address, Governance, + GovernanceConfig, }, proposal::{get_proposal_address, Proposal}, proposal_instruction::{ @@ -55,8 +57,8 @@ use crate::program_test::{cookies::SignatoryRecordCookie, tools::clone_keypair}; use self::{ cookies::{ GovernanceCookie, GovernedAccountCookie, GovernedMintCookie, GovernedProgramCookie, - ProposalCookie, ProposalInstructionCookie, RealmCookie, TokeOwnerRecordCookie, - VoteRecordCookie, + GovernedTokenCookie, ProposalCookie, ProposalInstructionCookie, RealmCookie, + TokeOwnerRecordCookie, VoteRecordCookie, }, tools::NopOverride, }; @@ -597,6 +599,40 @@ impl GovernanceProgramTest { } } + #[allow(dead_code)] + pub async fn with_governed_token(&mut self) -> GovernedTokenCookie { + let mint_keypair = Keypair::new(); + let mint_authority = Keypair::new(); + + self.create_mint(&mint_keypair, &mint_authority.pubkey()) + .await; + + let token_keypair = Keypair::new(); + let token_owner = Keypair::new(); + + self.create_empty_token_account( + &token_keypair, + &mint_keypair.pubkey(), + &token_owner.pubkey(), + ) + .await; + + self.mint_tokens( + &mint_keypair.pubkey(), + &mint_authority, + &token_keypair.pubkey(), + 100, + ) + .await; + + GovernedTokenCookie { + address: token_keypair.pubkey(), + token_owner: token_owner, + transfer_token_owner: true, + token_mint: mint_keypair.pubkey(), + } + } + pub fn get_default_governance_config( &mut self, realm_cookie: &RealmCookie, @@ -863,6 +899,73 @@ impl GovernanceProgramTest { }) } + #[allow(dead_code)] + pub async fn with_token_governance( + &mut self, + realm_cookie: &RealmCookie, + governed_token_cookie: &GovernedTokenCookie, + ) -> Result { + self.with_token_governance_using_instruction( + realm_cookie, + governed_token_cookie, + NopOverride, + None, + ) + .await + } + + #[allow(dead_code)] + pub async fn with_token_governance_using_instruction( + &mut self, + realm_cookie: &RealmCookie, + governed_token_cookie: &GovernedTokenCookie, + instruction_override: F, + signers_override: Option<&[&Keypair]>, + ) -> Result { + let config = GovernanceConfig { + realm: realm_cookie.address, + governed_account: governed_token_cookie.address, + min_tokens_to_create_proposal: 5, + min_instruction_hold_up_time: 10, + max_voting_time: 100, + yes_vote_threshold_percentage: 60, + }; + + let mut create_token_governance_instruction = create_token_governance( + &self.program_id, + &governed_token_cookie.token_owner.pubkey(), + &self.context.payer.pubkey(), + config.clone(), + governed_token_cookie.transfer_token_owner, + ); + + instruction_override(&mut create_token_governance_instruction); + + let default_signers = &[&governed_token_cookie.token_owner]; + let singers = signers_override.unwrap_or(default_signers); + + self.process_transaction(&[create_token_governance_instruction], Some(singers)) + .await?; + + let account = Governance { + account_type: GovernanceAccountType::TokenGovernance, + config, + proposals_count: 0, + }; + + let token_governance_address = get_token_governance_address( + &self.program_id, + &realm_cookie.address, + &governed_token_cookie.address, + ); + + Ok(GovernanceCookie { + address: token_governance_address, + account, + next_proposal_index: 0, + }) + } + #[allow(dead_code)] pub async fn with_proposal( &mut self, @@ -934,6 +1037,8 @@ impl GovernanceProgramTest { ) .await?; + let clock = self.get_clock().await; + let account = Proposal { account_type: GovernanceAccountType::Proposal, description_link, @@ -943,7 +1048,7 @@ impl GovernanceProgramTest { state: ProposalState::Draft, signatories_count: 0, // Clock always returns 1 when running under the test - draft_at: 1, + draft_at: clock.unix_timestamp, signing_off_at: None, voting_at: None, voting_completed_at: None, @@ -1233,6 +1338,41 @@ impl GovernanceProgramTest { .await } + #[allow(dead_code)] + pub async fn with_transfer_tokens_instruction( + &mut self, + governed_token_cookie: &GovernedTokenCookie, + proposal_cookie: &mut ProposalCookie, + token_owner_record_cookie: &TokeOwnerRecordCookie, + index: Option, + ) -> Result { + let token_account_keypair = Keypair::new(); + self.create_empty_token_account( + &token_account_keypair, + &governed_token_cookie.token_mint, + &self.context.payer.pubkey(), + ) + .await; + + let mut instruction = spl_token::instruction::transfer( + &spl_token::id(), + &governed_token_cookie.address, + &token_account_keypair.pubkey(), + &proposal_cookie.account.governance, + &[], + 15, + ) + .unwrap(); + + self.with_instruction_impl( + proposal_cookie, + token_owner_record_cookie, + index, + &mut instruction, + ) + .await + } + #[allow(dead_code)] pub async fn with_upgrade_program_instruction( &mut self, @@ -1505,6 +1645,39 @@ impl GovernanceProgramTest { .expect(format!("GET-TEST-ACCOUNT-ERROR: Account {}", address).as_str()) } + #[allow(dead_code)] + pub async fn get_clock(&mut self) -> Clock { + self.get_bincode_account::(&sysvar::clock::id()) + .await + } + + #[allow(dead_code)] + pub async fn advance_clock_past_timestamp(&mut self, unix_timestamp: UnixTimestamp) { + let mut clock = self.get_clock().await; + let mut n = 1; + + while clock.unix_timestamp <= unix_timestamp { + // Since the exact time is not deterministic keep wrapping by arbitrary 400 slots until we pass the requested timestamp + self.context.warp_to_slot(clock.slot + n * 400).unwrap(); + + n = n + 1; + clock = self.get_clock().await; + } + } + + #[allow(dead_code)] + pub async fn advance_clock_by_min_timespan(&mut self, time_span: u64) { + let clock = self.get_clock().await; + self.advance_clock_past_timestamp(clock.unix_timestamp + (time_span as i64)) + .await; + } + + #[allow(dead_code)] + pub async fn advance_clock(&mut self) { + let clock = self.get_clock().await; + self.context.warp_to_slot(clock.slot + 2).unwrap(); + } + #[allow(dead_code)] pub async fn get_upgradable_loader_account( &mut self, diff --git a/stake-pool/cli/README.md b/stake-pool/cli/README.md index 1016e9e4357..13b1ead38e6 100644 --- a/stake-pool/cli/README.md +++ b/stake-pool/cli/README.md @@ -1,3 +1,58 @@ # SPL Stake Pool program command-line utility -A basic command-line for creating and using SPL Stake Pools. See https://spl.solana.com/stake-pool for more details +A basic command-line for creating and using SPL Stake Pools. See https://spl.solana.com/stake-pool for more details. + +## Scripts for setting up a stake pool + +Under `./scripts`, this repo also contains some bash scripts that are useful for +setting up your own stake pool. These scripts require the Solana CLI tool suite, +which can be downloaded by following the instructions at +(https://docs.solana.com/cli/install-solana-cli-tools). Additionally, you must +have a usable keypair, created at the default location using `solana-keygen new`. + +### setup-local.sh + +Builds the stake pool program and sets up a `solana-test-validator` with some +new validator vote accounts. + +The only input it accepts is a number, for the number of vote accounts to create, e.g.: + +```bash +$ ./setup-local.sh 100 +``` + +#### Important notes on local network + +If you are using epochs of 32 slots, there is a good chance +that you will pass an epoch while using one of the stake pool commands, causing +it to fail with: `Custom program error: 0x11`. This is totally normal, and will +not happen on a normal network. + +Since there is no voting activity on the test validator network, you will +need to use the `--force` flag with `solana delegate-stake`, ie: + +```bash +$ solana delegate-stake --force stake.json CzDy6uxLTko5Jjcdm46AozMmrARY6R2aDBagdemiBuiT +``` + +### setup-stake-pool.sh + +Creates a new stake pool with the parameters hardcoded in the script: + +* fee numerator +* fee denominator +* maximum number of validators +* list of validator vote accounts + +```bash +$ ./setup-stake-pool.sh 100 validator_list.txt +``` + +### deposit-withdraw.sh + +Creates new stakes to deposit into each of the stake accounts in the pool, given +the stake pool, mint, and validator list. + +```bash +$ ./deposit-withdraw.sh keys/stake-pool.json validator_list.txt +``` diff --git a/stake-pool/cli/scripts/deposit-withdraw.sh b/stake-pool/cli/scripts/deposit-withdraw.sh new file mode 100755 index 00000000000..f8508135143 --- /dev/null +++ b/stake-pool/cli/scripts/deposit-withdraw.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash + +# Script to deposit and withdraw stakes from a pool, given stake pool public key +# and a list of validators + +cd "$(dirname "$0")" +stake_pool_keyfile=$1 +validator_list=$2 + +stake_pool_pubkey=$(solana-keygen pubkey $stake_pool_keyfile) + +sol_amount=2 +half_sol_amount=1 +keys_dir=keys +spl_stake_pool=../../../target/debug/spl-stake-pool + +mkdir -p $keys_dir + +create_keypair () { + if test ! -f $1 + then + solana-keygen new --no-passphrase -s -o $1 + fi +} + +create_user_stakes () { + validator_list=$1 + sol_amount=$2 + for validator in $(cat $validator_list) + do + create_keypair $keys_dir/stake_$validator.json + solana create-stake-account $keys_dir/stake_$validator.json $sol_amount + done +} + +delegate_user_stakes () { + validator_list=$1 + for validator in $(cat $validator_list) + do + solana delegate-stake --force $keys_dir/stake_$validator.json $validator + done +} + +deposit_stakes () { + stake_pool_pubkey=$1 + validator_list=$2 + for validator in $(cat $validator_list) + do + stake=$(solana-keygen pubkey $keys_dir/stake_$validator.json) + $spl_stake_pool deposit $stake_pool_pubkey $stake + done +} + +withdraw_stakes () { + stake_pool_pubkey=$1 + validator_list=$2 + pool_amount=$3 + for validator in $(cat $validator_list) + do + $spl_stake_pool withdraw $stake_pool_pubkey $pool_amount --vote-account $validator + done +} + +echo "Creating user stake accounts" +create_user_stakes $validator_list $sol_amount +echo "Delegating user stakes" +delegate_user_stakes $validator_list +echo "Waiting for stakes to activate, this may take awhile depending on the network!" +echo "If you are running on localnet with 32 slots per epoch, wait 24 seconds..." +sleep 24 +echo "Depositing stakes into stake pool" +deposit_stakes $stake_pool_pubkey $validator_list +echo "Withdrawing stakes from stake pool" +withdraw_stakes $stake_pool_pubkey $validator_list $half_sol_amount diff --git a/stake-pool/cli/scripts/setup-local.sh b/stake-pool/cli/scripts/setup-local.sh new file mode 100755 index 00000000000..1aeb2474676 --- /dev/null +++ b/stake-pool/cli/scripts/setup-local.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash + +# Script to setup a local solana-test-validator with the stake pool program + +cd "$(dirname "$0")" +max_validators=$1 +validator_list=$2 + +keys_dir=keys +mkdir -p $keys_dir +if test -f $validator_list +then + rm $validator_list +fi + +create_keypair () { + if test ! -f $1 + then + solana-keygen new --no-passphrase -s -o $1 + fi +} + +build_program () { + cargo build-bpf --manifest-path ../../program/Cargo.toml +} + +setup_validator() { + solana-test-validator --bpf-program SPoo1XJbrC5pXDfg5NQAXo2RKyfimXKm6KpqicGvpbo ../../../target/deploy/spl_stake_pool.so --quiet --reset --slots-per-epoch 32 & + pid=$! + solana config set --url http://127.0.0.1:8899 + solana config set --commitment confirmed + echo "waiting for solana-test-validator, pid: $pid" + sleep 5 +} + +create_vote_accounts () { + max_validators=$1 + validator_list=$2 + for number in $(seq 1 $max_validators) + do + create_keypair $keys_dir/identity_$number.json + create_keypair $keys_dir/vote_$number.json + solana create-vote-account $keys_dir/vote_$number.json $keys_dir/identity_$number.json --commission 1 + vote_pubkey=$(solana-keygen pubkey $keys_dir/vote_$number.json) + echo $vote_pubkey >> $validator_list + done +} + +echo "Building on-chain program" +build_program + +echo "Setting up local validator" +setup_validator + +echo "Creating vote accounts" +create_vote_accounts $max_validators $validator_list diff --git a/stake-pool/cli/scripts/setup-stake-pool.sh b/stake-pool/cli/scripts/setup-stake-pool.sh new file mode 100755 index 00000000000..f0b8c34969d --- /dev/null +++ b/stake-pool/cli/scripts/setup-stake-pool.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash + +# Script to setup a stake pool, add new validators from a list + +cd "$(dirname "$0")" +max_validators=$1 +validator_list=$2 + +keys_dir=keys +spl_stake_pool=../../../target/debug/spl-stake-pool + +mkdir -p $keys_dir + +build_cli () { + cargo build --manifest-path ../Cargo.toml +} + +create_keypair () { + if test ! -f $1 + then + solana-keygen new --no-passphrase -s -o $1 + fi +} + +setup_pool () { + max_validators=$1 + stake_pool_keyfile=$2 + mint_keyfile=$3 + mkdir -p $keys_dir + create_keypair $stake_pool_keyfile + create_keypair $mint_keyfile + + $spl_stake_pool create-pool --fee-numerator 3 --fee-denominator 100 \ + --max-validators $max_validators \ + --pool-keypair $stake_pool_keyfile \ + --mint-keypair $mint_keyfile +} + +create_validator_stakes() { + pool=$1 + validator_list=$2 + for validator in $(cat $validator_list) + do + $spl_stake_pool create-validator-stake $pool $validator + done +} + +add_validator_stakes () { + pool=$1 + validator_list=$2 + for validator in $(cat $validator_list) + do + $spl_stake_pool add-validator $pool $validator + done +} + +stake_pool_keyfile=$keys_dir/stake-pool.json +mint_keyfile=$keys_dir/mint.json + +echo "Building CLI" +build_cli +echo "Creating pool" +setup_pool $max_validators $stake_pool_keyfile $mint_keyfile + +stake_pool_pubkey=$(solana-keygen pubkey $stake_pool_keyfile) + +echo "Creating validator stake accounts" +create_validator_stakes $stake_pool_pubkey $validator_list +echo "Adding validator stake accounts to the pool" +add_validator_stakes $stake_pool_pubkey $validator_list diff --git a/stake-pool/cli/src/main.rs b/stake-pool/cli/src/main.rs index 613d0f0c2fe..ff9340de334 100644 --- a/stake-pool/cli/src/main.rs +++ b/stake-pool/cli/src/main.rs @@ -27,6 +27,7 @@ use { commitment_config::CommitmentConfig, native_token::{self, Sol}, signature::{Keypair, Signer}, + signers::Signers, system_instruction, transaction::Transaction, }, @@ -82,6 +83,20 @@ fn check_fee_payer_balance(config: &Config, required_balance: u64) -> Result<(), } } +fn send_transaction_no_wait( + config: &Config, + transaction: Transaction, +) -> solana_client::client_error::Result<()> { + if config.dry_run { + let result = config.rpc_client.simulate_transaction(&transaction)?; + println!("Simulate result: {:?}", result); + } else { + let signature = config.rpc_client.send_transaction(&transaction)?; + println!("Signature: {}", signature); + } + Ok(()) +} + fn send_transaction( config: &Config, transaction: Transaction, @@ -98,6 +113,23 @@ fn send_transaction( Ok(()) } +fn checked_transaction_with_signers( + config: &Config, + instructions: &[Instruction], + signers: &T, +) -> Result { + let (recent_blockhash, fee_calculator) = config.rpc_client.get_recent_blockhash()?; + let transaction = Transaction::new_signed_with_payer( + instructions, + Some(&config.fee_payer.pubkey()), + signers, + recent_blockhash, + ); + + check_fee_payer_balance(config, fee_calculator.calculate_fee(transaction.message()))?; + Ok(transaction) +} + fn command_create_pool( config: &Config, deposit_authority: Option, @@ -105,9 +137,10 @@ fn command_create_pool( max_validators: u32, stake_pool_keypair: Option, mint_keypair: Option, + reserve_keypair: Option, ) -> CommandResult { - let reserve_stake = Keypair::new(); - println!("Creating reserve stake {}", reserve_stake.pubkey()); + let reserve_keypair = reserve_keypair.unwrap_or_else(Keypair::new); + println!("Creating reserve stake {}", reserve_keypair.pubkey()); let mint_keypair = mint_keypair.unwrap_or_else(Keypair::new); println!("Creating mint {}", mint_keypair.pubkey()); @@ -163,13 +196,13 @@ fn command_create_pool( // Account for the stake pool reserve system_instruction::create_account( &config.fee_payer.pubkey(), - &reserve_stake.pubkey(), + &reserve_keypair.pubkey(), reserve_stake_balance, STAKE_STATE_LEN as u64, &stake_program::id(), ), stake_program::initialize( - &reserve_stake.pubkey(), + &reserve_keypair.pubkey(), &stake_program::Authorized { staker: withdraw_authority, withdrawer: withdraw_authority, @@ -236,7 +269,7 @@ fn command_create_pool( &config.manager.pubkey(), &config.staker.pubkey(), &validator_list.pubkey(), - &reserve_stake.pubkey(), + &reserve_keypair.pubkey(), &mint_keypair.pubkey(), &pool_fee_account.pubkey(), &spl_token::id(), @@ -259,7 +292,7 @@ fn command_create_pool( config.fee_payer.as_ref(), &mint_keypair, &pool_fee_account, - &reserve_stake, + &reserve_keypair, ]; unique_signers!(setup_signers); setup_transaction.sign(&setup_signers, recent_blockhash); @@ -284,8 +317,8 @@ fn command_vsa_create( vote_account: &Pubkey, ) -> CommandResult { println!("Creating stake account on {}", vote_account); - - let mut transaction = Transaction::new_with_payer( + let transaction = checked_transaction_with_signers( + config, &[ // Create new validator stake account address spl_stake_pool::instruction::create_validator_stake_account_with_vote( @@ -296,15 +329,8 @@ fn command_vsa_create( vote_account, ), ], - Some(&config.fee_payer.pubkey()), - ); - - let (recent_blockhash, fee_calculator) = config.rpc_client.get_recent_blockhash()?; - check_fee_payer_balance(config, fee_calculator.calculate_fee(transaction.message()))?; - transaction.sign( &[config.fee_payer.as_ref(), config.staker.as_ref()], - recent_blockhash, - ); + )?; send_transaction(config, transaction)?; Ok(()) } @@ -344,21 +370,21 @@ fn command_vsa_add( command_update(config, stake_pool_address, false, false)?; } - let instruction = spl_stake_pool::instruction::add_validator_to_pool_with_vote( - &spl_stake_pool::id(), - &stake_pool, - stake_pool_address, - vote_account, - ); - - let mut transaction = - Transaction::new_with_payer(&[instruction], Some(&config.fee_payer.pubkey())); - - let (recent_blockhash, fee_calculator) = config.rpc_client.get_recent_blockhash()?; - check_fee_payer_balance(config, fee_calculator.calculate_fee(transaction.message()))?; let mut signers = vec![config.fee_payer.as_ref(), config.staker.as_ref()]; unique_signers!(signers); - transaction.sign(&signers, recent_blockhash); + let transaction = checked_transaction_with_signers( + config, + &[ + spl_stake_pool::instruction::add_validator_to_pool_with_vote( + &spl_stake_pool::id(), + &stake_pool, + stake_pool_address, + vote_account, + ), + ], + &signers, + )?; + send_transaction(config, transaction)?; Ok(()) } @@ -378,7 +404,10 @@ fn command_vsa_remove( let staker_pubkey = config.staker.pubkey(); let new_authority = new_authority.as_ref().unwrap_or(&staker_pubkey); - let mut transaction = Transaction::new_with_payer( + let mut signers = vec![config.fee_payer.as_ref(), config.staker.as_ref()]; + unique_signers!(signers); + let transaction = checked_transaction_with_signers( + config, &[ // Create new validator stake account address spl_stake_pool::instruction::remove_validator_from_pool_with_vote( @@ -389,15 +418,8 @@ fn command_vsa_remove( new_authority, ), ], - Some(&config.fee_payer.pubkey()), - ); - - let (recent_blockhash, fee_calculator) = config.rpc_client.get_recent_blockhash()?; - check_fee_payer_balance(config, fee_calculator.calculate_fee(transaction.message()))?; - transaction.sign( - &[config.fee_payer.as_ref(), config.staker.as_ref()], - recent_blockhash, - ); + &signers, + )?; send_transaction(config, transaction)?; Ok(()) } @@ -414,23 +436,22 @@ fn command_increase_validator_stake( } let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?; - let instruction = spl_stake_pool::instruction::increase_validator_stake_with_vote( - &spl_stake_pool::id(), - &stake_pool, - stake_pool_address, - vote_account, - lamports, - ); - let mut transaction = - Transaction::new_with_payer(&[instruction], Some(&config.fee_payer.pubkey())); - - let (recent_blockhash, fee_calculator) = config.rpc_client.get_recent_blockhash()?; - check_fee_payer_balance(config, fee_calculator.calculate_fee(transaction.message()))?; - transaction.sign( - &[config.fee_payer.as_ref(), config.staker.as_ref()], - recent_blockhash, - ); + let mut signers = vec![config.fee_payer.as_ref(), config.staker.as_ref()]; + unique_signers!(signers); + let transaction = checked_transaction_with_signers( + config, + &[ + spl_stake_pool::instruction::increase_validator_stake_with_vote( + &spl_stake_pool::id(), + &stake_pool, + stake_pool_address, + vote_account, + lamports, + ), + ], + &signers, + )?; send_transaction(config, transaction)?; Ok(()) } @@ -447,23 +468,22 @@ fn command_decrease_validator_stake( } let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?; - let instruction = spl_stake_pool::instruction::decrease_validator_stake_with_vote( - &spl_stake_pool::id(), - &stake_pool, - stake_pool_address, - vote_account, - lamports, - ); - - let mut transaction = - Transaction::new_with_payer(&[instruction], Some(&config.fee_payer.pubkey())); - let (recent_blockhash, fee_calculator) = config.rpc_client.get_recent_blockhash()?; - check_fee_payer_balance(config, fee_calculator.calculate_fee(transaction.message()))?; - transaction.sign( - &[config.fee_payer.as_ref(), config.staker.as_ref()], - recent_blockhash, - ); + let mut signers = vec![config.fee_payer.as_ref(), config.staker.as_ref()]; + unique_signers!(signers); + let transaction = checked_transaction_with_signers( + config, + &[ + spl_stake_pool::instruction::decrease_validator_stake_with_vote( + &spl_stake_pool::id(), + &stake_pool, + stake_pool_address, + vote_account, + lamports, + ), + ], + &signers, + )?; send_transaction(config, transaction)?; Ok(()) } @@ -475,7 +495,10 @@ fn command_set_preferred_validator( vote_address: Option, ) -> CommandResult { let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?; - let mut transaction = Transaction::new_with_payer( + let mut signers = vec![config.fee_payer.as_ref(), config.staker.as_ref()]; + unique_signers!(signers); + let transaction = checked_transaction_with_signers( + config, &[spl_stake_pool::instruction::set_preferred_validator( &spl_stake_pool::id(), stake_pool_address, @@ -484,14 +507,8 @@ fn command_set_preferred_validator( preferred_type, vote_address, )], - Some(&config.fee_payer.pubkey()), - ); - - let (recent_blockhash, fee_calculator) = config.rpc_client.get_recent_blockhash()?; - check_fee_payer_balance(config, fee_calculator.calculate_fee(transaction.message()))?; - let mut signers = vec![config.fee_payer.as_ref(), config.staker.as_ref()]; - unique_signers!(signers); - transaction.sign(&signers, recent_blockhash); + &signers, + )?; send_transaction(config, transaction)?; Ok(()) } @@ -640,7 +657,14 @@ fn command_list(config: &Config, stake_pool_address: &Pubkey) -> CommandResult { let pool_mint = get_token_mint(&config.rpc_client, &stake_pool.pool_mint)?; let epoch_info = config.rpc_client.get_epoch_info()?; - for validator in validator_list.validators { + let reserve_stake = config.rpc_client.get_account(&stake_pool.reserve_stake)?; + println!( + "Reserve Account: {}\tBalance: {}", + stake_pool.reserve_stake, + Sol(reserve_stake.lamports), + ); + + for validator in &validator_list.validators { println!( "Validator Vote Account: {}\tBalance: {}\tLast Update Epoch: {}{}", validator.vote_account_address, @@ -667,6 +691,14 @@ fn command_list(config: &Config, stake_pool_address: &Pubkey) -> CommandResult { "Total Pool Tokens: {}", spl_token::amount_to_ui_amount(stake_pool.pool_token_supply, pool_mint.decimals) ); + println!( + "Total number of validators: {}", + validator_list.validators.len() + ); + println!( + "Max number of validators: {}", + validator_list.max_validators + ); if config.verbose { println!(); @@ -723,24 +755,43 @@ fn command_update( let validator_list = get_validator_list(&config.rpc_client, &stake_pool.validator_list)?; - let instructions = spl_stake_pool::instruction::update_stake_pool( - &spl_stake_pool::id(), - &stake_pool, - &validator_list, - stake_pool_address, - no_merge, - ); + let (mut update_list_instructions, update_balance_instruction) = + spl_stake_pool::instruction::update_stake_pool( + &spl_stake_pool::id(), + &stake_pool, + &validator_list, + stake_pool_address, + no_merge, + ); - // TODO: A faster solution would be to send all the `update_validator_list_balance` instructions concurrently - for instruction in instructions { - let mut transaction = - Transaction::new_with_payer(&[instruction], Some(&config.fee_payer.pubkey())); + let update_list_instructions_len = update_list_instructions.len(); + if update_list_instructions_len > 0 { + let last_instruction = update_list_instructions.split_off(update_list_instructions_len - 1); + // send the first ones without waiting + for instruction in update_list_instructions { + let transaction = checked_transaction_with_signers( + config, + &[instruction], + &[config.fee_payer.as_ref()], + )?; + send_transaction_no_wait(config, transaction)?; + } - let (recent_blockhash, fee_calculator) = config.rpc_client.get_recent_blockhash()?; - check_fee_payer_balance(config, fee_calculator.calculate_fee(transaction.message()))?; - transaction.sign(&[config.fee_payer.as_ref()], recent_blockhash); + // wait on the last one + let transaction = checked_transaction_with_signers( + config, + &last_instruction, + &[config.fee_payer.as_ref()], + )?; send_transaction(config, transaction)?; } + let transaction = checked_transaction_with_signers( + config, + &[update_balance_instruction], + &[config.fee_payer.as_ref()], + )?; + send_transaction(config, transaction)?; + Ok(()) } @@ -1009,7 +1060,10 @@ fn command_set_manager( } }; - let mut transaction = Transaction::new_with_payer( + let mut signers = vec![config.fee_payer.as_ref(), config.manager.as_ref()]; + unique_signers!(signers); + let transaction = checked_transaction_with_signers( + config, &[spl_stake_pool::instruction::set_manager( &spl_stake_pool::id(), stake_pool_address, @@ -1017,14 +1071,8 @@ fn command_set_manager( &new_manager, &new_fee_receiver, )], - Some(&config.fee_payer.pubkey()), - ); - - let (recent_blockhash, fee_calculator) = config.rpc_client.get_recent_blockhash()?; - check_fee_payer_balance(config, fee_calculator.calculate_fee(transaction.message()))?; - let mut signers = vec![config.fee_payer.as_ref(), config.manager.as_ref()]; - unique_signers!(signers); - transaction.sign(&signers, recent_blockhash); + &signers, + )?; send_transaction(config, transaction)?; Ok(()) } @@ -1034,41 +1082,35 @@ fn command_set_staker( stake_pool_address: &Pubkey, new_staker: &Pubkey, ) -> CommandResult { - let mut transaction = Transaction::new_with_payer( + let mut signers = vec![config.fee_payer.as_ref(), config.manager.as_ref()]; + unique_signers!(signers); + let transaction = checked_transaction_with_signers( + config, &[spl_stake_pool::instruction::set_staker( &spl_stake_pool::id(), stake_pool_address, &config.manager.pubkey(), new_staker, )], - Some(&config.fee_payer.pubkey()), - ); - - let (recent_blockhash, fee_calculator) = config.rpc_client.get_recent_blockhash()?; - check_fee_payer_balance(config, fee_calculator.calculate_fee(transaction.message()))?; - let mut signers = vec![config.fee_payer.as_ref(), config.manager.as_ref()]; - unique_signers!(signers); - transaction.sign(&signers, recent_blockhash); + &signers, + )?; send_transaction(config, transaction)?; Ok(()) } fn command_set_fee(config: &Config, stake_pool_address: &Pubkey, new_fee: Fee) -> CommandResult { - let mut transaction = Transaction::new_with_payer( + let mut signers = vec![config.fee_payer.as_ref(), config.manager.as_ref()]; + unique_signers!(signers); + let transaction = checked_transaction_with_signers( + config, &[spl_stake_pool::instruction::set_fee( &spl_stake_pool::id(), stake_pool_address, &config.manager.pubkey(), new_fee, )], - Some(&config.fee_payer.pubkey()), - ); - - let (recent_blockhash, fee_calculator) = config.rpc_client.get_recent_blockhash()?; - check_fee_payer_balance(config, fee_calculator.calculate_fee(transaction.message()))?; - let mut signers = vec![config.fee_payer.as_ref(), config.manager.as_ref()]; - unique_signers!(signers); - transaction.sign(&signers, recent_blockhash); + &signers, + )?; send_transaction(config, transaction)?; Ok(()) } @@ -1241,6 +1283,14 @@ fn main() { .takes_value(true) .help("Stake pool mint keypair [default: new keypair]"), ) + .arg( + Arg::with_name("reserve_keypair") + .long("reserve-keypair") + .validator(is_keypair_or_ask_keyword) + .value_name("PATH") + .takes_value(true) + .help("Stake pool reserve keypair [default: new keypair]"), + ) ) .subcommand(SubCommand::with_name("create-validator-stake") .about("Create a new stake account to use with the pool. Must be signed by the pool staker.") @@ -1710,6 +1760,7 @@ fn main() { let max_validators = value_t_or_exit!(arg_matches, "max_validators", u32); let pool_keypair = keypair_of(arg_matches, "pool_keypair"); let mint_keypair = keypair_of(arg_matches, "mint_keypair"); + let reserve_keypair = keypair_of(arg_matches, "reserve_keypair"); command_create_pool( &config, deposit_authority, @@ -1720,6 +1771,7 @@ fn main() { max_validators, pool_keypair, mint_keypair, + reserve_keypair, ) } ("create-validator-stake", Some(arg_matches)) => { diff --git a/stake-pool/program/src/instruction.rs b/stake-pool/program/src/instruction.rs index de3108878bf..a1b013cc680 100644 --- a/stake-pool/program/src/instruction.rs +++ b/stake-pool/program/src/instruction.rs @@ -162,7 +162,11 @@ pub enum StakePoolInstruction { /// 10. `[]` Stake Config sysvar /// 11. `[]` System program /// 12. `[]` Stake program - /// userdata: amount of lamports to split into the transient stake account + /// userdata: amount of lamports to increase on the given validator. + /// The actual amount split into the transient stake account is: + /// `lamports + stake_rent_exemption` + /// The rent-exemption of the stake account is withdrawn back to the reserve + /// after it is merged. IncreaseValidatorStake(u64), /// (Staker only) Set the preferred deposit or withdraw stake account for the @@ -734,7 +738,7 @@ pub fn update_stake_pool( validator_list: &ValidatorList, stake_pool_address: &Pubkey, no_merge: bool, -) -> Vec { +) -> (Vec, Instruction) { let vote_accounts: Vec = validator_list .validators .iter() @@ -744,10 +748,10 @@ pub fn update_stake_pool( let (withdraw_authority, _) = find_withdraw_authority_program_address(program_id, stake_pool_address); - let mut instructions: Vec = vec![]; + let mut update_list_instructions: Vec = vec![]; let mut start_index = 0; for accounts_chunk in vote_accounts.chunks(MAX_VALIDATORS_TO_UPDATE) { - instructions.push(update_validator_list_balance( + update_list_instructions.push(update_validator_list_balance( program_id, stake_pool_address, &withdraw_authority, @@ -760,7 +764,7 @@ pub fn update_stake_pool( start_index += MAX_VALIDATORS_TO_UPDATE as u32; } - instructions.push(update_stake_pool_balance( + let update_balance_instruction = update_stake_pool_balance( program_id, stake_pool_address, &withdraw_authority, @@ -768,8 +772,8 @@ pub fn update_stake_pool( &stake_pool.reserve_stake, &stake_pool.manager_fee_account, &stake_pool.pool_mint, - )); - instructions + ); + (update_list_instructions, update_balance_instruction) } /// Creates instructions required to deposit into a stake pool, given a stake diff --git a/stake-pool/program/src/lib.rs b/stake-pool/program/src/lib.rs index f1b539e19a1..6ac5c78870c 100644 --- a/stake-pool/program/src/lib.rs +++ b/stake-pool/program/src/lib.rs @@ -33,7 +33,7 @@ pub const MINIMUM_ACTIVE_STAKE: u64 = LAMPORTS_PER_SOL; /// Maximum amount of validator stake accounts to update per /// `UpdateValidatorListBalance` instruction, based on compute limits -pub const MAX_VALIDATORS_TO_UPDATE: usize = 10; +pub const MAX_VALIDATORS_TO_UPDATE: usize = 5; /// Get the stake amount under consideration when calculating pool token /// conversions diff --git a/stake-pool/program/src/processor.rs b/stake-pool/program/src/processor.rs index 7778e27b083..acc1ee0a092 100644 --- a/stake-pool/program/src/processor.rs +++ b/stake-pool/program/src/processor.rs @@ -1150,19 +1150,21 @@ impl Processor { } let stake_rent = rent.minimum_balance(std::mem::size_of::()); - let minimum_lamports = MINIMUM_ACTIVE_STAKE + stake_rent; - if lamports < minimum_lamports { + if lamports < MINIMUM_ACTIVE_STAKE { msg!( "Need more than {} lamports for transient stake to be rent-exempt and mergeable, {} provided", - minimum_lamports, + MINIMUM_ACTIVE_STAKE, lamports ); return Err(ProgramError::AccountNotRentExempt); } + // the stake account rent exemption is withdrawn after the merge, so + let total_lamports = lamports.saturating_add(stake_rent); + if reserve_stake_account_info .lamports() - .saturating_sub(lamports) + .saturating_sub(total_lamports) <= stake_rent { let max_split_amount = reserve_stake_account_info @@ -1189,7 +1191,7 @@ impl Processor { withdraw_authority_info.clone(), AUTHORITY_WITHDRAW, stake_pool.withdraw_bump_seed, - lamports, + total_lamports, transient_stake_account_info.clone(), )?; @@ -1206,7 +1208,7 @@ impl Processor { stake_pool.withdraw_bump_seed, )?; - validator_list_entry.transient_stake_lamports = lamports; + validator_list_entry.transient_stake_lamports = total_lamports; validator_list.serialize(&mut *validator_list_info.data.borrow_mut())?; Ok(()) @@ -1404,6 +1406,9 @@ impl Processor { if stake_program::active_stakes_can_merge(&stake, &validator_stake) .is_ok() { + let additional_lamports = transient_stake_info + .lamports() + .saturating_sub(stake.delegation.stake); Self::stake_merge( stake_pool_info.key, transient_stake_info.clone(), @@ -1415,6 +1420,23 @@ impl Processor { stake_history_info.clone(), stake_program_info.clone(), )?; + + // post merge of two active stakes, withdraw + // the extra back to the reserve + if additional_lamports > 0 { + Self::stake_withdraw( + stake_pool_info.key, + validator_stake_info.clone(), + withdraw_authority_info.clone(), + AUTHORITY_WITHDRAW, + stake_pool.withdraw_bump_seed, + reserve_stake_info.clone(), + clock_info.clone(), + stake_history_info.clone(), + stake_program_info.clone(), + additional_lamports, + )?; + } } else { msg!("Stake activating or just active, not ready to merge"); transient_stake_lamports = account_stake; @@ -1436,6 +1458,10 @@ impl Processor { // Status for validator stake // * active -> do everything // * any other state / not a stake -> error state, but account for transient stake + let validator_stake_state = try_from_slice_unchecked::( + &validator_stake_info.data.borrow(), + ) + .ok(); match validator_stake_state { Some(stake_program::StakeState::Stake(_, stake)) => { if validator_stake_record.status == StakeStatus::Active { @@ -1782,8 +1808,8 @@ impl Processor { /// Processes [Withdraw](enum.Instruction.html). fn process_withdraw( program_id: &Pubkey, - pool_tokens: u64, accounts: &[AccountInfo], + pool_tokens: u64, ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let stake_pool_info = next_account_info(account_info_iter)?; @@ -2126,7 +2152,7 @@ impl Processor { } StakePoolInstruction::Withdraw(amount) => { msg!("Instruction: Withdraw"); - Self::process_withdraw(program_id, amount, accounts) + Self::process_withdraw(program_id, accounts, amount) } StakePoolInstruction::SetManager => { msg!("Instruction: SetManager"); diff --git a/stake-pool/program/tests/increase.rs b/stake-pool/program/tests/increase.rs index 191c10a6c02..b67273524b0 100644 --- a/stake-pool/program/tests/increase.rs +++ b/stake-pool/program/tests/increase.rs @@ -93,8 +93,8 @@ async fn success() { assert!(transient_account.is_none()); let rent = banks_client.get_rent().await.unwrap(); - let lamports = rent.minimum_balance(std::mem::size_of::()); - let reserve_lamports = reserve_lamports - lamports; + let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let increase_amount = reserve_lamports - stake_rent - 1; let error = stake_pool_accounts .increase_validator_stake( &mut banks_client, @@ -102,7 +102,7 @@ async fn success() { &recent_blockhash, &validator_stake.transient_stake_account, &validator_stake.vote.pubkey(), - reserve_lamports, + increase_amount, ) .await; assert!(error.is_none()); @@ -116,7 +116,7 @@ async fn success() { let reserve_stake_state = deserialize::(&reserve_stake_account.data).unwrap(); assert_eq!( - pre_reserve_stake_account.lamports - reserve_lamports, + pre_reserve_stake_account.lamports - increase_amount - stake_rent, reserve_stake_account.lamports ); assert!(reserve_stake_state.delegation().is_none()); @@ -126,7 +126,10 @@ async fn success() { get_account(&mut banks_client, &validator_stake.transient_stake_account).await; let transient_stake_state = deserialize::(&transient_stake_account.data).unwrap(); - assert_eq!(transient_stake_account.lamports, reserve_lamports); + assert_eq!( + transient_stake_account.lamports, + increase_amount + stake_rent + ); assert_ne!( transient_stake_state.delegation().unwrap().activation_epoch, Epoch::MAX @@ -332,7 +335,7 @@ async fn fail_with_small_lamport_amount() { ) = setup().await; let rent = banks_client.get_rent().await.unwrap(); - let lamports = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); let error = stake_pool_accounts .increase_validator_stake( @@ -341,7 +344,7 @@ async fn fail_with_small_lamport_amount() { &recent_blockhash, &validator_stake.transient_stake_account, &validator_stake.vote.pubkey(), - lamports, + stake_rent, ) .await .unwrap() diff --git a/stake-pool/program/tests/update_validator_list_balance.rs b/stake-pool/program/tests/update_validator_list_balance.rs index e266ce7cc6f..b5e46dc7d53 100644 --- a/stake-pool/program/tests/update_validator_list_balance.rs +++ b/stake-pool/program/tests/update_validator_list_balance.rs @@ -381,14 +381,6 @@ async fn merge_into_validator_stake() { let stake_pool = try_from_slice_unchecked::(&stake_pool_info.data).unwrap(); assert_eq!(expected_lamports, stake_pool.total_stake_lamports); - let stake_pool_info = get_account( - &mut context.banks_client, - &stake_pool_accounts.stake_pool.pubkey(), - ) - .await; - let stake_pool = try_from_slice_unchecked::(&stake_pool_info.data).unwrap(); - assert_eq!(expected_lamports, stake_pool.total_stake_lamports); - // Warp one more epoch so the stakes activate, ready to merge let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch; slot += slots_per_epoch; diff --git a/token-lending/js/package-lock.json b/token-lending/js/package-lock.json index 41990876183..0f40f65a730 100644 --- a/token-lending/js/package-lock.json +++ b/token-lending/js/package-lock.json @@ -205,9 +205,9 @@ } }, "@solana/web3.js": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.19.0.tgz", - "integrity": "sha512-tZ+Bk0sD6xvyHas13eRTl0fOtWGHoKithNjIHHYeUbZWwdxOKdutfv14SuD1eqkBdoiQs3KFJvVUkj3M7zTVDg==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.20.0.tgz", + "integrity": "sha512-s/hmbeC7h0QeMKDHl5HTJbY4NSrT3IK1oGerNwdmFfnbs9ygtBbXo1MXaeYVDcNazsthKgiYE+unNd+cpVH8HA==", "requires": { "@babel/runtime": "^7.12.5", "bn.js": "^5.0.0", @@ -298,9 +298,9 @@ "dev": true }, "@types/express-serve-static-core": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.21.tgz", - "integrity": "sha512-gwCiEZqW6f7EoR8TTEfalyEhb1zA5jQJnRngr97+3pzMaO1RKoI1w2bw07TK72renMUVWcWS5mLI6rk1NqN0nA==", + "version": "4.17.22", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.22.tgz", + "integrity": "sha512-WdqmrUsRS4ootGha6tVwk/IVHM1iorU8tGehftQD2NWiPniw/sm7xdJOIlXLwqdInL9wBw/p7oO8vaYEF3NDmA==", "requires": { "@types/node": "*", "@types/qs": "*", @@ -337,9 +337,9 @@ } }, "@types/node": { - "version": "15.12.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.4.tgz", - "integrity": "sha512-zrNj1+yqYF4WskCMOHwN+w9iuD12+dGm0rQ35HLl9/Ouuq52cEtd0CH9qMgrdNmi5ejC1/V7vKEXYubB+65DkA==" + "version": "15.12.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.5.tgz", + "integrity": "sha512-se3yX7UHv5Bscf8f1ERKvQOD6sTyycH3hdaoozvaLxgUiY5lIGEeH37AD0G0Qi9kPqihPn0HOfd2yaIEN9VwEg==" }, "@types/prettier": { "version": "2.3.0", @@ -388,6 +388,14 @@ } } }, + "@types/ws": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.5.tgz", + "integrity": "sha512-8mbDgtc8xpxDDem5Gwj76stBDJX35KQ3YBoayxlqUQcL5BZUthiqP/VQ4PQnLHqM4PmlbyO74t98eJpURO+gPA==", + "requires": { + "@types/node": "*" + } + }, "@typescript-eslint/eslint-plugin": { "version": "4.28.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.0.tgz", @@ -429,41 +437,41 @@ } }, "@typescript-eslint/parser": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.28.0.tgz", - "integrity": "sha512-7x4D22oPY8fDaOCvkuXtYYTQ6mTMmkivwEzS+7iml9F9VkHGbbZ3x4fHRwxAb5KeuSkLqfnYjs46tGx2Nour4A==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.28.1.tgz", + "integrity": "sha512-UjrMsgnhQIIK82hXGaD+MCN8IfORS1CbMdu7VlZbYa8LCZtbZjJA26De4IPQB7XYZbL8gJ99KWNj0l6WD0guJg==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "4.28.0", - "@typescript-eslint/types": "4.28.0", - "@typescript-eslint/typescript-estree": "4.28.0", + "@typescript-eslint/scope-manager": "4.28.1", + "@typescript-eslint/types": "4.28.1", + "@typescript-eslint/typescript-estree": "4.28.1", "debug": "^4.3.1" }, "dependencies": { "@typescript-eslint/scope-manager": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.0.tgz", - "integrity": "sha512-eCALCeScs5P/EYjwo6se9bdjtrh8ByWjtHzOkC4Tia6QQWtQr3PHovxh3TdYTuFcurkYI4rmFsRFpucADIkseg==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.1.tgz", + "integrity": "sha512-o95bvGKfss6705x7jFGDyS7trAORTy57lwJ+VsYwil/lOUxKQ9tA7Suuq+ciMhJc/1qPwB3XE2DKh9wubW8YYA==", "dev": true, "requires": { - "@typescript-eslint/types": "4.28.0", - "@typescript-eslint/visitor-keys": "4.28.0" + "@typescript-eslint/types": "4.28.1", + "@typescript-eslint/visitor-keys": "4.28.1" } }, "@typescript-eslint/types": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.0.tgz", - "integrity": "sha512-p16xMNKKoiJCVZY5PW/AfILw2xe1LfruTcfAKBj3a+wgNYP5I9ZEKNDOItoRt53p4EiPV6iRSICy8EPanG9ZVA==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.1.tgz", + "integrity": "sha512-4z+knEihcyX7blAGi7O3Fm3O6YRCP+r56NJFMNGsmtdw+NCdpG5SgNz427LS9nQkRVTswZLhz484hakQwB8RRg==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.0.tgz", - "integrity": "sha512-m19UQTRtxMzKAm8QxfKpvh6OwQSXaW1CdZPoCaQuLwAq7VZMNuhJmZR4g5281s2ECt658sldnJfdpSZZaxUGMQ==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.1.tgz", + "integrity": "sha512-GhKxmC4sHXxHGJv8e8egAZeTZ6HI4mLU6S7FUzvFOtsk7ZIDN1ksA9r9DyOgNqowA9yAtZXV0Uiap61bIO81FQ==", "dev": true, "requires": { - "@typescript-eslint/types": "4.28.0", - "@typescript-eslint/visitor-keys": "4.28.0", + "@typescript-eslint/types": "4.28.1", + "@typescript-eslint/visitor-keys": "4.28.1", "debug": "^4.3.1", "globby": "^11.0.3", "is-glob": "^4.0.1", @@ -472,12 +480,12 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.0.tgz", - "integrity": "sha512-PjJyTWwrlrvM5jazxYF5ZPs/nl0kHDZMVbuIcbpawVXaDPelp3+S9zpOz5RmVUfS/fD5l5+ZXNKnWhNYjPzCvw==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.1.tgz", + "integrity": "sha512-K4HMrdFqr9PFquPu178SaSb92CaWe2yErXyPumc8cYWxFmhgJsNY9eSePmO05j0JhBvf2Cdhptd6E6Yv9HVHcg==", "dev": true, "requires": { - "@typescript-eslint/types": "4.28.0", + "@typescript-eslint/types": "4.28.1", "eslint-visitor-keys": "^2.0.0" } } @@ -938,6 +946,11 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "delay": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", + "integrity": "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==" + }, "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -1626,22 +1639,31 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "isomorphic-ws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", + "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==" + }, "jayson": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/jayson/-/jayson-3.6.3.tgz", - "integrity": "sha512-H/JuWKaJwU8FbwofPHROvtGoMF6R3DB0GGPpYyIaRzXU50Ser/4likFVfo/bpTGe0csB7n/+uybxJpBvX40VOQ==", + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/jayson/-/jayson-3.6.4.tgz", + "integrity": "sha512-GH63DsRFFlodS8krFgAhxwYvQFmSwjsFxKnPrHQtp+BJj/tpeSj3hyBGGqmTkuq043U1Gn6u8VdsVRFZX1EEiQ==", "requires": { "@types/connect": "^3.4.33", "@types/express-serve-static-core": "^4.17.9", "@types/lodash": "^4.14.159", "@types/node": "^12.12.54", + "@types/ws": "^7.4.4", "JSONStream": "^1.3.5", "commander": "^2.20.3", + "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", + "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "lodash": "^4.17.20", - "uuid": "^3.4.0" + "uuid": "^3.4.0", + "ws": "^7.4.5" }, "dependencies": { "@types/node": { @@ -2116,9 +2138,9 @@ } }, "rollup": { - "version": "2.52.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.2.tgz", - "integrity": "sha512-4RlFC3k2BIHlUsJ9mGd8OO+9Lm2eDF5P7+6DNQOp5sx+7N/1tFM01kELfbxlMX3MxT6owvLB1ln4S3QvvQlbUA==", + "version": "2.52.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.3.tgz", + "integrity": "sha512-QF3Sju8Kl2z0osI4unyOLyUudyhOMK6G0AeqJWgfiyigqLAlnNrfBcDWDx+f1cqn+JU2iIYVkDrgQ6/KtwEfrg==", "dev": true, "requires": { "fsevents": "~2.3.2" @@ -2657,9 +2679,9 @@ "dev": true }, "ws": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.0.tgz", - "integrity": "sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgi4vw==" + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.1.tgz", + "integrity": "sha512-2c6faOUH/nhoQN6abwMloF7Iyl0ZS2E9HGtsiLrWn0zOOMWlhtDmdf/uihDt6jnuCxgtwGBNy6Onsoy2s2O2Ow==" }, "yallist": { "version": "4.0.0", diff --git a/token-lending/js/package.json b/token-lending/js/package.json index f9ed59bbef4..79696a85909 100644 --- a/token-lending/js/package.json +++ b/token-lending/js/package.json @@ -35,7 +35,7 @@ }, "dependencies": { "@solana/spl-token": "0.1.6", - "@solana/web3.js": "^1.19.0", + "@solana/web3.js": "^1.20.0", "bn.js": "^5.2.0", "mkdirp": "^1.0.4" }, @@ -47,17 +47,17 @@ "@types/eslint-plugin-prettier": "^3.1.0", "@types/mkdirp": "^1.0.1", "@types/mz": "^2.7.3", - "@types/node": "^15.12.4", + "@types/node": "^15.12.5", "@types/prettier": "^2.3.0", "@types/rollup-plugin-json": "^3.0.2", "@typescript-eslint/eslint-plugin": "^4.28.0", - "@typescript-eslint/parser": "^4.28.0", + "@typescript-eslint/parser": "^4.28.1", "dotenv": "^10.0.0", "eslint": "^7.29.0", "eslint-config-prettier": "^7.2.0", "eslint-plugin-prettier": "^3.4.0", "prettier": "^2.2.1", - "rollup": "^2.52.2", + "rollup": "^2.52.3", "rollup-plugin-json": "^4.0.0", "rollup-plugin-node-resolve": "^5.2.0", "rollup-plugin-sourcemaps": "^0.6.3", diff --git a/token-swap/js/package-lock.json b/token-swap/js/package-lock.json index 82b37447c62..105722cabda 100644 --- a/token-swap/js/package-lock.json +++ b/token-swap/js/package-lock.json @@ -354,9 +354,9 @@ } }, "@solana/web3.js": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.19.0.tgz", - "integrity": "sha512-tZ+Bk0sD6xvyHas13eRTl0fOtWGHoKithNjIHHYeUbZWwdxOKdutfv14SuD1eqkBdoiQs3KFJvVUkj3M7zTVDg==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.20.0.tgz", + "integrity": "sha512-s/hmbeC7h0QeMKDHl5HTJbY4NSrT3IK1oGerNwdmFfnbs9ygtBbXo1MXaeYVDcNazsthKgiYE+unNd+cpVH8HA==", "requires": { "@babel/runtime": "^7.12.5", "bn.js": "^5.0.0", @@ -437,9 +437,9 @@ } }, "@types/express-serve-static-core": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.21.tgz", - "integrity": "sha512-gwCiEZqW6f7EoR8TTEfalyEhb1zA5jQJnRngr97+3pzMaO1RKoI1w2bw07TK72renMUVWcWS5mLI6rk1NqN0nA==", + "version": "4.17.22", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.22.tgz", + "integrity": "sha512-WdqmrUsRS4ootGha6tVwk/IVHM1iorU8tGehftQD2NWiPniw/sm7xdJOIlXLwqdInL9wBw/p7oO8vaYEF3NDmA==", "requires": { "@types/node": "*", "@types/qs": "*", @@ -496,6 +496,14 @@ "@types/node": "*" } }, + "@types/ws": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.5.tgz", + "integrity": "sha512-8mbDgtc8xpxDDem5Gwj76stBDJX35KQ3YBoayxlqUQcL5BZUthiqP/VQ4PQnLHqM4PmlbyO74t98eJpURO+gPA==", + "requires": { + "@types/node": "*" + } + }, "JSONStream": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", @@ -1058,6 +1066,11 @@ "object-keys": "^1.0.12" } }, + "delay": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", + "integrity": "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==" + }, "deprecation": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", @@ -2256,22 +2269,31 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "isomorphic-ws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", + "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==" + }, "jayson": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/jayson/-/jayson-3.6.3.tgz", - "integrity": "sha512-H/JuWKaJwU8FbwofPHROvtGoMF6R3DB0GGPpYyIaRzXU50Ser/4likFVfo/bpTGe0csB7n/+uybxJpBvX40VOQ==", + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/jayson/-/jayson-3.6.4.tgz", + "integrity": "sha512-GH63DsRFFlodS8krFgAhxwYvQFmSwjsFxKnPrHQtp+BJj/tpeSj3hyBGGqmTkuq043U1Gn6u8VdsVRFZX1EEiQ==", "requires": { "@types/connect": "^3.4.33", "@types/express-serve-static-core": "^4.17.9", "@types/lodash": "^4.14.159", "@types/node": "^12.12.54", + "@types/ws": "^7.4.4", "JSONStream": "^1.3.5", "commander": "^2.20.3", + "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", + "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "lodash": "^4.17.20", - "uuid": "^3.4.0" + "uuid": "^3.4.0", + "ws": "^7.4.5" }, "dependencies": { "@types/node": { @@ -2865,9 +2887,9 @@ "dev": true }, "prettier": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.1.tgz", - "integrity": "sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", + "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==", "dev": true }, "process-nextick-args": { @@ -3794,9 +3816,9 @@ "dev": true }, "ws": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.0.tgz", - "integrity": "sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgi4vw==" + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.1.tgz", + "integrity": "sha512-2c6faOUH/nhoQN6abwMloF7Iyl0ZS2E9HGtsiLrWn0zOOMWlhtDmdf/uihDt6jnuCxgtwGBNy6Onsoy2s2O2Ow==" }, "y18n": { "version": "4.0.1", diff --git a/token/js/package-lock.json b/token/js/package-lock.json index 79cd6f907f6..3538323534c 100644 --- a/token/js/package-lock.json +++ b/token/js/package-lock.json @@ -114,9 +114,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.5.tgz", - "integrity": "sha512-kixrYn4JwfAVPa0f2yfzc2AWti6WRRyO3XjWW5PJAvtE11qhSayrrcrEnee05KAtNaPC+EwehE8Qt1UedEVB8w==", + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.7.tgz", + "integrity": "sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw==", "dev": true, "engines": { "node": ">=6.9.0" @@ -588,14 +588,14 @@ } }, "node_modules/@babel/node": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/node/-/node-7.14.5.tgz", - "integrity": "sha512-fwAf0Ba3gjcwP04T0BD0Y3oHOTDQH2tVcyaPOMjb+8xkCJ38FoaVOlLfQ/asBgm6aCkBkn12/l8spCXUEEeAyA==", + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/node/-/node-7.14.7.tgz", + "integrity": "sha512-QghINtVgOUCrSpE7z4IXPTGJfRYyjoY7ny5Qz0cuUlsThQv6WSn1xJk217WlEDucPLP2o5HwGXPSkxD4LWOZxQ==", "dev": true, "dependencies": { "@babel/register": "^7.14.5", "commander": "^4.0.1", - "core-js": "^3.14.0", + "core-js": "^3.15.0", "node-environment-flags": "^1.0.5", "regenerator-runtime": "^0.13.4", "v8flags": "^3.1.1" @@ -649,9 +649,9 @@ } }, "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.14.5.tgz", - "integrity": "sha512-tbD/CG3l43FIXxmu4a7RBe4zH7MLJ+S/lFowPFO7HetS2hyOZ/0nnnznegDuzFzfkyQYTxqdTH/hKmuBngaDAA==", + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.14.7.tgz", + "integrity": "sha512-RK8Wj7lXLY3bqei69/cc25gwS5puEc3dknoFPFbqfy3XxYQBQFvu4ioWpafMBAB+L9NyptQK4nMOa5Xz16og8Q==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.14.5", @@ -795,12 +795,12 @@ } }, "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.14.5.tgz", - "integrity": "sha512-VzMyY6PWNPPT3pxc5hi9LloKNr4SSrVCg7Yr6aZpW4Ym07r7KqSU/QXYwjXLVxqwSv0t/XSXkFoKBPUkZ8vb2A==", + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.14.7.tgz", + "integrity": "sha512-082hsZz+sVabfmDWo1Oct1u1AgbKbUAyVgmX4otIc7bdsRgHBXwTwb3DpDmD4Eyyx6DNiuz5UAATT655k+kL5g==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.14.5", + "@babel/compat-data": "^7.14.7", "@babel/helper-compilation-targets": "^7.14.5", "@babel/helper-plugin-utils": "^7.14.5", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", @@ -1184,9 +1184,9 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.14.5.tgz", - "integrity": "sha512-wU9tYisEbRMxqDezKUqC9GleLycCRoUsai9ddlsq54r8QRLaeEhc+d+9DqCG+kV9W2GgQjTZESPTpn5bAFMDww==", + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.14.7.tgz", + "integrity": "sha512-0mDE99nK+kVh3xlc5vKwB6wnP9ecuSj+zQCa/n0voENtP/zymdT4HH6QEb65wjjcbqr1Jb/7z9Qp7TF5FtwYGw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" @@ -1393,9 +1393,9 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.14.5.tgz", - "integrity": "sha512-+Xe5+6MWFo311U8SchgeX5c1+lJM+eZDBZgD+tvXu9VVQPXwwVzeManMMjYX6xw2HczngfOSZjoFYKwdeB/Jvw==", + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.14.7.tgz", + "integrity": "sha512-DTNOTaS7TkW97xsDMrp7nycUVh6sn/eq22VaxWfEdzuEbRsiaOU0pqU7DlyUGHVsbQbSghvjKRpEl+nUCKGQSg==", "dev": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.14.5" @@ -1635,17 +1635,17 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.14.5.tgz", - "integrity": "sha512-ci6TsS0bjrdPpWGnQ+m4f+JSSzDKlckqKIJJt9UZ/+g7Zz9k0N8lYU8IeLg/01o2h8LyNZDMLGgRLDTxpudLsA==", + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.14.7.tgz", + "integrity": "sha512-itOGqCKLsSUl0Y+1nSfhbuuOlTs0MJk2Iv7iSH+XT/mR8U1zRLO7NjWlYXB47yhK4J/7j+HYty/EhFZDYKa/VA==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.14.5", + "@babel/compat-data": "^7.14.7", "@babel/helper-compilation-targets": "^7.14.5", "@babel/helper-plugin-utils": "^7.14.5", "@babel/helper-validator-option": "^7.14.5", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.14.5", - "@babel/plugin-proposal-async-generator-functions": "^7.14.5", + "@babel/plugin-proposal-async-generator-functions": "^7.14.7", "@babel/plugin-proposal-class-properties": "^7.14.5", "@babel/plugin-proposal-class-static-block": "^7.14.5", "@babel/plugin-proposal-dynamic-import": "^7.14.5", @@ -1654,7 +1654,7 @@ "@babel/plugin-proposal-logical-assignment-operators": "^7.14.5", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.5", "@babel/plugin-proposal-numeric-separator": "^7.14.5", - "@babel/plugin-proposal-object-rest-spread": "^7.14.5", + "@babel/plugin-proposal-object-rest-spread": "^7.14.7", "@babel/plugin-proposal-optional-catch-binding": "^7.14.5", "@babel/plugin-proposal-optional-chaining": "^7.14.5", "@babel/plugin-proposal-private-methods": "^7.14.5", @@ -1680,7 +1680,7 @@ "@babel/plugin-transform-block-scoping": "^7.14.5", "@babel/plugin-transform-classes": "^7.14.5", "@babel/plugin-transform-computed-properties": "^7.14.5", - "@babel/plugin-transform-destructuring": "^7.14.5", + "@babel/plugin-transform-destructuring": "^7.14.7", "@babel/plugin-transform-dotall-regex": "^7.14.5", "@babel/plugin-transform-duplicate-keys": "^7.14.5", "@babel/plugin-transform-exponentiation-operator": "^7.14.5", @@ -1692,7 +1692,7 @@ "@babel/plugin-transform-modules-commonjs": "^7.14.5", "@babel/plugin-transform-modules-systemjs": "^7.14.5", "@babel/plugin-transform-modules-umd": "^7.14.5", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.14.5", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.14.7", "@babel/plugin-transform-new-target": "^7.14.5", "@babel/plugin-transform-object-super": "^7.14.5", "@babel/plugin-transform-parameters": "^7.14.5", @@ -1700,7 +1700,7 @@ "@babel/plugin-transform-regenerator": "^7.14.5", "@babel/plugin-transform-reserved-words": "^7.14.5", "@babel/plugin-transform-shorthand-properties": "^7.14.5", - "@babel/plugin-transform-spread": "^7.14.5", + "@babel/plugin-transform-spread": "^7.14.6", "@babel/plugin-transform-sticky-regex": "^7.14.5", "@babel/plugin-transform-template-literals": "^7.14.5", "@babel/plugin-transform-typeof-symbol": "^7.14.5", @@ -1711,7 +1711,7 @@ "babel-plugin-polyfill-corejs2": "^0.2.2", "babel-plugin-polyfill-corejs3": "^0.2.2", "babel-plugin-polyfill-regenerator": "^0.2.2", - "core-js-compat": "^3.14.0", + "core-js-compat": "^3.15.0", "semver": "^6.3.0" }, "engines": { @@ -2313,9 +2313,9 @@ } }, "node_modules/@solana/web3.js": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.18.0.tgz", - "integrity": "sha512-ijAoRd4Sje1QYoPAwDr7KYlDK40FE7tAUa2V3wT4PGKatWf4ETDXoyYlW89J6vrqOT+mV3GUuaVC76tOFlrXyA==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.20.0.tgz", + "integrity": "sha512-s/hmbeC7h0QeMKDHl5HTJbY4NSrT3IK1oGerNwdmFfnbs9ygtBbXo1MXaeYVDcNazsthKgiYE+unNd+cpVH8HA==", "dependencies": { "@babel/runtime": "^7.12.5", "bn.js": "^5.0.0", @@ -2500,13 +2500,13 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.0.tgz", - "integrity": "sha512-KcF6p3zWhf1f8xO84tuBailV5cN92vhS+VT7UJsPzGBm9VnQqfI9AsiMUFUCYHTYPg1uCCo+HyiDnpDuvkAMfQ==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.1.tgz", + "integrity": "sha512-9yfcNpDaNGQ6/LQOX/KhUFTR1sCKH+PBr234k6hI9XJ0VP5UqGxap0AnNwBnWFk1MNyWBylJH9ZkzBXC+5akZQ==", "dev": true, "dependencies": { - "@typescript-eslint/experimental-utils": "4.28.0", - "@typescript-eslint/scope-manager": "4.28.0", + "@typescript-eslint/experimental-utils": "4.28.1", + "@typescript-eslint/scope-manager": "4.28.1", "debug": "^4.3.1", "functional-red-black-tree": "^1.0.1", "regexpp": "^3.1.0", @@ -2530,53 +2530,6 @@ } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.0.tgz", - "integrity": "sha512-eCALCeScs5P/EYjwo6se9bdjtrh8ByWjtHzOkC4Tia6QQWtQr3PHovxh3TdYTuFcurkYI4rmFsRFpucADIkseg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "4.28.0", - "@typescript-eslint/visitor-keys": "4.28.0" - }, - "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.0.tgz", - "integrity": "sha512-p16xMNKKoiJCVZY5PW/AfILw2xe1LfruTcfAKBj3a+wgNYP5I9ZEKNDOItoRt53p4EiPV6iRSICy8EPanG9ZVA==", - "dev": true, - "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.0.tgz", - "integrity": "sha512-PjJyTWwrlrvM5jazxYF5ZPs/nl0kHDZMVbuIcbpawVXaDPelp3+S9zpOz5RmVUfS/fD5l5+ZXNKnWhNYjPzCvw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "4.28.0", - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/debug": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", @@ -2589,15 +2542,6 @@ "node": ">=6.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -2620,15 +2564,15 @@ } }, "node_modules/@typescript-eslint/experimental-utils": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.0.tgz", - "integrity": "sha512-9XD9s7mt3QWMk82GoyUpc/Ji03vz4T5AYlHF9DcoFNfJ/y3UAclRsfGiE2gLfXtyC+JRA3trR7cR296TEb1oiQ==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.1.tgz", + "integrity": "sha512-n8/ggadrZ+uyrfrSEchx3jgODdmcx7MzVM2sI3cTpI/YlfSm0+9HEUaWw3aQn2urL2KYlWYMDgn45iLfjDYB+Q==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.7", - "@typescript-eslint/scope-manager": "4.28.0", - "@typescript-eslint/types": "4.28.0", - "@typescript-eslint/typescript-estree": "4.28.0", + "@typescript-eslint/scope-manager": "4.28.1", + "@typescript-eslint/types": "4.28.1", + "@typescript-eslint/typescript-estree": "4.28.1", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -2643,97 +2587,6 @@ "eslint": "*" } }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/scope-manager": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.0.tgz", - "integrity": "sha512-eCALCeScs5P/EYjwo6se9bdjtrh8ByWjtHzOkC4Tia6QQWtQr3PHovxh3TdYTuFcurkYI4rmFsRFpucADIkseg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "4.28.0", - "@typescript-eslint/visitor-keys": "4.28.0" - }, - "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/types": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.0.tgz", - "integrity": "sha512-p16xMNKKoiJCVZY5PW/AfILw2xe1LfruTcfAKBj3a+wgNYP5I9ZEKNDOItoRt53p4EiPV6iRSICy8EPanG9ZVA==", - "dev": true, - "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.0.tgz", - "integrity": "sha512-m19UQTRtxMzKAm8QxfKpvh6OwQSXaW1CdZPoCaQuLwAq7VZMNuhJmZR4g5281s2ECt658sldnJfdpSZZaxUGMQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "4.28.0", - "@typescript-eslint/visitor-keys": "4.28.0", - "debug": "^4.3.1", - "globby": "^11.0.3", - "is-glob": "^4.0.1", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.0.tgz", - "integrity": "sha512-PjJyTWwrlrvM5jazxYF5ZPs/nl0kHDZMVbuIcbpawVXaDPelp3+S9zpOz5RmVUfS/fD5l5+ZXNKnWhNYjPzCvw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "4.28.0", - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/@typescript-eslint/experimental-utils/node_modules/eslint-utils": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", @@ -2761,74 +2614,15 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/@typescript-eslint/parser": { - "version": "4.27.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.27.0.tgz", - "integrity": "sha512-XpbxL+M+gClmJcJ5kHnUpBGmlGdgNvy6cehgR6ufyxkEJMGP25tZKCaKyC0W/JVpuhU3VU1RBn7SYUPKSMqQvQ==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.28.1.tgz", + "integrity": "sha512-UjrMsgnhQIIK82hXGaD+MCN8IfORS1CbMdu7VlZbYa8LCZtbZjJA26De4IPQB7XYZbL8gJ99KWNj0l6WD0guJg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "4.27.0", - "@typescript-eslint/types": "4.27.0", - "@typescript-eslint/typescript-estree": "4.27.0", + "@typescript-eslint/scope-manager": "4.28.1", + "@typescript-eslint/types": "4.28.1", + "@typescript-eslint/typescript-estree": "4.28.1", "debug": "^4.3.1" }, "engines": { @@ -2866,13 +2660,13 @@ "dev": true }, "node_modules/@typescript-eslint/scope-manager": { - "version": "4.27.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.27.0.tgz", - "integrity": "sha512-DY73jK6SEH6UDdzc6maF19AHQJBFVRf6fgAXHPXCGEmpqD4vYgPEzqpFz1lf/daSbOcMpPPj9tyXXDPW2XReAw==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.1.tgz", + "integrity": "sha512-o95bvGKfss6705x7jFGDyS7trAORTy57lwJ+VsYwil/lOUxKQ9tA7Suuq+ciMhJc/1qPwB3XE2DKh9wubW8YYA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.27.0", - "@typescript-eslint/visitor-keys": "4.27.0" + "@typescript-eslint/types": "4.28.1", + "@typescript-eslint/visitor-keys": "4.28.1" }, "engines": { "node": "^8.10.0 || ^10.13.0 || >=11.10.1" @@ -2883,9 +2677,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "4.27.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.27.0.tgz", - "integrity": "sha512-I4ps3SCPFCKclRcvnsVA/7sWzh7naaM/b4pBO2hVxnM3wrU51Lveybdw5WoIktU/V4KfXrTt94V9b065b/0+wA==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.1.tgz", + "integrity": "sha512-4z+knEihcyX7blAGi7O3Fm3O6YRCP+r56NJFMNGsmtdw+NCdpG5SgNz427LS9nQkRVTswZLhz484hakQwB8RRg==", "dev": true, "engines": { "node": "^8.10.0 || ^10.13.0 || >=11.10.1" @@ -2896,13 +2690,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "4.27.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.27.0.tgz", - "integrity": "sha512-KH03GUsUj41sRLLEy2JHstnezgpS5VNhrJouRdmh6yNdQ+yl8w5LrSwBkExM+jWwCJa7Ct2c8yl8NdtNRyQO6g==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.1.tgz", + "integrity": "sha512-GhKxmC4sHXxHGJv8e8egAZeTZ6HI4mLU6S7FUzvFOtsk7ZIDN1ksA9r9DyOgNqowA9yAtZXV0Uiap61bIO81FQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.27.0", - "@typescript-eslint/visitor-keys": "4.27.0", + "@typescript-eslint/types": "4.28.1", + "@typescript-eslint/visitor-keys": "4.28.1", "debug": "^4.3.1", "globby": "^11.0.3", "is-glob": "^4.0.1", @@ -2940,9 +2734,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/globby": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", - "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", "dev": true, "dependencies": { "array-union": "^2.1.0", @@ -2999,12 +2793,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "4.27.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.27.0.tgz", - "integrity": "sha512-es0GRYNZp0ieckZ938cEANfEhsfHrzuLrePukLKtY3/KPXcq1Xd555Mno9/GOgXhKzn0QfkDLVgqWO3dGY80bg==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.1.tgz", + "integrity": "sha512-K4HMrdFqr9PFquPu178SaSb92CaWe2yErXyPumc8cYWxFmhgJsNY9eSePmO05j0JhBvf2Cdhptd6E6Yv9HVHcg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.27.0", + "@typescript-eslint/types": "4.28.1", "eslint-visitor-keys": "^2.0.0" }, "engines": { @@ -4338,9 +4132,9 @@ } }, "node_modules/core-js": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.14.0.tgz", - "integrity": "sha512-3s+ed8er9ahK+zJpp9ZtuVcDoFzHNiZsPbNAAE4KXgrRHbjSqqNN6xGSXq6bq7TZIbKj4NLrLb6bJ5i+vSVjHA==", + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.15.1.tgz", + "integrity": "sha512-h8VbZYnc9pDzueiS2610IULDkpFFPunHwIpl8yRwFahAEEdSpHlTy3h3z3rKq5h11CaUdBEeRViu9AYvbxiMeg==", "dev": true, "hasInstallScript": true, "funding": { @@ -4349,9 +4143,9 @@ } }, "node_modules/core-js-compat": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.14.0.tgz", - "integrity": "sha512-R4NS2eupxtiJU+VwgkF9WTpnSfZW4pogwKHd8bclWU2sp93Pr5S1uYJI84cMOubJRou7bcfL0vmwtLslWN5p3A==", + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.15.1.tgz", + "integrity": "sha512-xGhzYMX6y7oEGQGAJmP2TmtBLvR4nZmRGEcFa3ubHOq5YEp51gGN9AovVa0AoujGZIq+Wm6dISiYyGNfdflYww==", "dev": true, "dependencies": { "browserslist": "^4.16.6", @@ -4786,9 +4580,9 @@ } }, "node_modules/eslint": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.28.0.tgz", - "integrity": "sha512-UMfH0VSjP0G4p3EWirscJEQ/cHqnT/iuH6oNZOB94nBjWbMnhGEPxsZm1eyIW0C/9jLI0Fow4W5DXLjEI7mn1g==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.29.0.tgz", + "integrity": "sha512-82G/JToB9qIy/ArBzIWG9xvvwL3R86AlCjtGw+A29OMZDqhTybz/MByORSukGxeI+YPCR4coYyITKk8BFH9nDA==", "dev": true, "dependencies": { "@babel/code-frame": "7.12.11", @@ -4880,9 +4674,9 @@ "dev": true }, "node_modules/eslint-plugin-flowtype": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-5.7.2.tgz", - "integrity": "sha512-7Oq/N0+3nijBnYWQYzz/Mp/7ZCpwxYvClRyW/PLAmimY9uLCBvoXsNsERcJdkKceyOjgRbFhhxs058KTrne9Mg==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-5.8.0.tgz", + "integrity": "sha512-feK1xnUTsMSNTOw9jFw7aVgZl7Ep+ghpta/YEoaV6jbXU6Yso30B7BIj9ObHLzZ5TFJL7D98az080wfykLCrcw==", "dev": true, "dependencies": { "lodash": "^4.17.15", @@ -5919,21 +5713,21 @@ } }, "node_modules/flow-parser": { - "version": "0.153.0", - "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.153.0.tgz", - "integrity": "sha512-qa7UODgbofQyruRWqNQ+KM5hO37CenByxhNfAztiwjBsPhWZ5AFh5g+gtLpTWPlzG7X66QdjBB9DuHNUBcaF+Q==", + "version": "0.154.0", + "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.154.0.tgz", + "integrity": "sha512-cH9xY/ljOgmqG1n7PU1jffiHhRggoloauwOrOlCWBEX4Y+ml6GA8g//tCVKU+6PO4BXoPF22TFHkS5E1bN3JOQ==", "dev": true, "engines": { "node": ">=0.4.0" } }, "node_modules/flow-remove-types": { - "version": "2.153.0", - "resolved": "https://registry.npmjs.org/flow-remove-types/-/flow-remove-types-2.153.0.tgz", - "integrity": "sha512-KHl0r0xH5VBMOtKmk2WJME3F4UDg+j0f/yH6lbBIGk3xKl5NTHLs+1h9murH9+KLxvVZ0ut5ydd/fx49RSqmEw==", + "version": "2.154.0", + "resolved": "https://registry.npmjs.org/flow-remove-types/-/flow-remove-types-2.154.0.tgz", + "integrity": "sha512-i55LkOLp4JRYrw11xiVx/GzY9IvnJORTEw6oKcoBKTQu8iULc+rnI4YlMSZpcki00Y3EOA/xk9XXLYt4obtfug==", "dev": true, "dependencies": { - "flow-parser": "^0.153.0", + "flow-parser": "^0.154.0", "pirates": "^3.0.2", "vlq": "^0.2.1" }, @@ -8787,9 +8581,9 @@ } }, "node_modules/prettier": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.1.tgz", - "integrity": "sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", + "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==", "dev": true, "bin": { "prettier": "bin-prettier.js" @@ -9241,9 +9035,9 @@ } }, "node_modules/rollup": { - "version": "2.52.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.0.tgz", - "integrity": "sha512-lSkBDGsVoXjqaBf7dsHwxBJz+p+hJEP72P+LOitA0yVs+Nzxj76FidkZE2thrmhjwGqLYiJo39opi7mAfaQ/Vg==", + "version": "2.52.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.4.tgz", + "integrity": "sha512-AXgUxxWXyGfsj8GKleR1k8KsG8G+7ZZDRU9RZb9PnLGSyTqI/1qf/+QSp1hRaR40j4yfBCKXs5khtGKiFwihfg==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -10556,9 +10350,9 @@ } }, "node_modules/typescript": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.2.tgz", - "integrity": "sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.4.tgz", + "integrity": "sha512-uauPG7XZn9F/mo+7MrsRjyvbxFpzemRjKEZXS4AK83oP2KKOJPvb+9cO/gmnv8arWZvhnjVOXz7B49m1l0e9Ew==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -11350,9 +11144,9 @@ } }, "@babel/compat-data": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.5.tgz", - "integrity": "sha512-kixrYn4JwfAVPa0f2yfzc2AWti6WRRyO3XjWW5PJAvtE11qhSayrrcrEnee05KAtNaPC+EwehE8Qt1UedEVB8w==", + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.7.tgz", + "integrity": "sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw==", "dev": true }, "@babel/core": { @@ -11707,14 +11501,14 @@ } }, "@babel/node": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/node/-/node-7.14.5.tgz", - "integrity": "sha512-fwAf0Ba3gjcwP04T0BD0Y3oHOTDQH2tVcyaPOMjb+8xkCJ38FoaVOlLfQ/asBgm6aCkBkn12/l8spCXUEEeAyA==", + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/node/-/node-7.14.7.tgz", + "integrity": "sha512-QghINtVgOUCrSpE7z4IXPTGJfRYyjoY7ny5Qz0cuUlsThQv6WSn1xJk217WlEDucPLP2o5HwGXPSkxD4LWOZxQ==", "dev": true, "requires": { "@babel/register": "^7.14.5", "commander": "^4.0.1", - "core-js": "^3.14.0", + "core-js": "^3.15.0", "node-environment-flags": "^1.0.5", "regenerator-runtime": "^0.13.4", "v8flags": "^3.1.1" @@ -11746,9 +11540,9 @@ } }, "@babel/plugin-proposal-async-generator-functions": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.14.5.tgz", - "integrity": "sha512-tbD/CG3l43FIXxmu4a7RBe4zH7MLJ+S/lFowPFO7HetS2hyOZ/0nnnznegDuzFzfkyQYTxqdTH/hKmuBngaDAA==", + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.14.7.tgz", + "integrity": "sha512-RK8Wj7lXLY3bqei69/cc25gwS5puEc3dknoFPFbqfy3XxYQBQFvu4ioWpafMBAB+L9NyptQK4nMOa5Xz16og8Q==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.14.5", @@ -11838,12 +11632,12 @@ } }, "@babel/plugin-proposal-object-rest-spread": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.14.5.tgz", - "integrity": "sha512-VzMyY6PWNPPT3pxc5hi9LloKNr4SSrVCg7Yr6aZpW4Ym07r7KqSU/QXYwjXLVxqwSv0t/XSXkFoKBPUkZ8vb2A==", + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.14.7.tgz", + "integrity": "sha512-082hsZz+sVabfmDWo1Oct1u1AgbKbUAyVgmX4otIc7bdsRgHBXwTwb3DpDmD4Eyyx6DNiuz5UAATT655k+kL5g==", "dev": true, "requires": { - "@babel/compat-data": "^7.14.5", + "@babel/compat-data": "^7.14.7", "@babel/helper-compilation-targets": "^7.14.5", "@babel/helper-plugin-utils": "^7.14.5", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", @@ -12101,9 +11895,9 @@ } }, "@babel/plugin-transform-destructuring": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.14.5.tgz", - "integrity": "sha512-wU9tYisEbRMxqDezKUqC9GleLycCRoUsai9ddlsq54r8QRLaeEhc+d+9DqCG+kV9W2GgQjTZESPTpn5bAFMDww==", + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.14.7.tgz", + "integrity": "sha512-0mDE99nK+kVh3xlc5vKwB6wnP9ecuSj+zQCa/n0voENtP/zymdT4HH6QEb65wjjcbqr1Jb/7z9Qp7TF5FtwYGw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.14.5" @@ -12232,9 +12026,9 @@ } }, "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.14.5.tgz", - "integrity": "sha512-+Xe5+6MWFo311U8SchgeX5c1+lJM+eZDBZgD+tvXu9VVQPXwwVzeManMMjYX6xw2HczngfOSZjoFYKwdeB/Jvw==", + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.14.7.tgz", + "integrity": "sha512-DTNOTaS7TkW97xsDMrp7nycUVh6sn/eq22VaxWfEdzuEbRsiaOU0pqU7DlyUGHVsbQbSghvjKRpEl+nUCKGQSg==", "dev": true, "requires": { "@babel/helper-create-regexp-features-plugin": "^7.14.5" @@ -12383,17 +12177,17 @@ } }, "@babel/preset-env": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.14.5.tgz", - "integrity": "sha512-ci6TsS0bjrdPpWGnQ+m4f+JSSzDKlckqKIJJt9UZ/+g7Zz9k0N8lYU8IeLg/01o2h8LyNZDMLGgRLDTxpudLsA==", + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.14.7.tgz", + "integrity": "sha512-itOGqCKLsSUl0Y+1nSfhbuuOlTs0MJk2Iv7iSH+XT/mR8U1zRLO7NjWlYXB47yhK4J/7j+HYty/EhFZDYKa/VA==", "dev": true, "requires": { - "@babel/compat-data": "^7.14.5", + "@babel/compat-data": "^7.14.7", "@babel/helper-compilation-targets": "^7.14.5", "@babel/helper-plugin-utils": "^7.14.5", "@babel/helper-validator-option": "^7.14.5", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.14.5", - "@babel/plugin-proposal-async-generator-functions": "^7.14.5", + "@babel/plugin-proposal-async-generator-functions": "^7.14.7", "@babel/plugin-proposal-class-properties": "^7.14.5", "@babel/plugin-proposal-class-static-block": "^7.14.5", "@babel/plugin-proposal-dynamic-import": "^7.14.5", @@ -12402,7 +12196,7 @@ "@babel/plugin-proposal-logical-assignment-operators": "^7.14.5", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.5", "@babel/plugin-proposal-numeric-separator": "^7.14.5", - "@babel/plugin-proposal-object-rest-spread": "^7.14.5", + "@babel/plugin-proposal-object-rest-spread": "^7.14.7", "@babel/plugin-proposal-optional-catch-binding": "^7.14.5", "@babel/plugin-proposal-optional-chaining": "^7.14.5", "@babel/plugin-proposal-private-methods": "^7.14.5", @@ -12428,7 +12222,7 @@ "@babel/plugin-transform-block-scoping": "^7.14.5", "@babel/plugin-transform-classes": "^7.14.5", "@babel/plugin-transform-computed-properties": "^7.14.5", - "@babel/plugin-transform-destructuring": "^7.14.5", + "@babel/plugin-transform-destructuring": "^7.14.7", "@babel/plugin-transform-dotall-regex": "^7.14.5", "@babel/plugin-transform-duplicate-keys": "^7.14.5", "@babel/plugin-transform-exponentiation-operator": "^7.14.5", @@ -12440,7 +12234,7 @@ "@babel/plugin-transform-modules-commonjs": "^7.14.5", "@babel/plugin-transform-modules-systemjs": "^7.14.5", "@babel/plugin-transform-modules-umd": "^7.14.5", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.14.5", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.14.7", "@babel/plugin-transform-new-target": "^7.14.5", "@babel/plugin-transform-object-super": "^7.14.5", "@babel/plugin-transform-parameters": "^7.14.5", @@ -12448,7 +12242,7 @@ "@babel/plugin-transform-regenerator": "^7.14.5", "@babel/plugin-transform-reserved-words": "^7.14.5", "@babel/plugin-transform-shorthand-properties": "^7.14.5", - "@babel/plugin-transform-spread": "^7.14.5", + "@babel/plugin-transform-spread": "^7.14.6", "@babel/plugin-transform-sticky-regex": "^7.14.5", "@babel/plugin-transform-template-literals": "^7.14.5", "@babel/plugin-transform-typeof-symbol": "^7.14.5", @@ -12459,7 +12253,7 @@ "babel-plugin-polyfill-corejs2": "^0.2.2", "babel-plugin-polyfill-corejs3": "^0.2.2", "babel-plugin-polyfill-regenerator": "^0.2.2", - "core-js-compat": "^3.14.0", + "core-js-compat": "^3.15.0", "semver": "^6.3.0" }, "dependencies": { @@ -12986,9 +12780,9 @@ "dev": true }, "@solana/web3.js": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.18.0.tgz", - "integrity": "sha512-ijAoRd4Sje1QYoPAwDr7KYlDK40FE7tAUa2V3wT4PGKatWf4ETDXoyYlW89J6vrqOT+mV3GUuaVC76tOFlrXyA==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.20.0.tgz", + "integrity": "sha512-s/hmbeC7h0QeMKDHl5HTJbY4NSrT3IK1oGerNwdmFfnbs9ygtBbXo1MXaeYVDcNazsthKgiYE+unNd+cpVH8HA==", "requires": { "@babel/runtime": "^7.12.5", "bn.js": "^5.0.0", @@ -13172,13 +12966,13 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.0.tgz", - "integrity": "sha512-KcF6p3zWhf1f8xO84tuBailV5cN92vhS+VT7UJsPzGBm9VnQqfI9AsiMUFUCYHTYPg1uCCo+HyiDnpDuvkAMfQ==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.1.tgz", + "integrity": "sha512-9yfcNpDaNGQ6/LQOX/KhUFTR1sCKH+PBr234k6hI9XJ0VP5UqGxap0AnNwBnWFk1MNyWBylJH9ZkzBXC+5akZQ==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "4.28.0", - "@typescript-eslint/scope-manager": "4.28.0", + "@typescript-eslint/experimental-utils": "4.28.1", + "@typescript-eslint/scope-manager": "4.28.1", "debug": "^4.3.1", "functional-red-black-tree": "^1.0.1", "regexpp": "^3.1.0", @@ -13186,32 +12980,6 @@ "tsutils": "^3.21.0" }, "dependencies": { - "@typescript-eslint/scope-manager": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.0.tgz", - "integrity": "sha512-eCALCeScs5P/EYjwo6se9bdjtrh8ByWjtHzOkC4Tia6QQWtQr3PHovxh3TdYTuFcurkYI4rmFsRFpucADIkseg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.28.0", - "@typescript-eslint/visitor-keys": "4.28.0" - } - }, - "@typescript-eslint/types": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.0.tgz", - "integrity": "sha512-p16xMNKKoiJCVZY5PW/AfILw2xe1LfruTcfAKBj3a+wgNYP5I9ZEKNDOItoRt53p4EiPV6iRSICy8EPanG9ZVA==", - "dev": true - }, - "@typescript-eslint/visitor-keys": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.0.tgz", - "integrity": "sha512-PjJyTWwrlrvM5jazxYF5ZPs/nl0kHDZMVbuIcbpawVXaDPelp3+S9zpOz5RmVUfS/fD5l5+ZXNKnWhNYjPzCvw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.28.0", - "eslint-visitor-keys": "^2.0.0" - } - }, "debug": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", @@ -13221,12 +12989,6 @@ "ms": "2.1.2" } }, - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -13245,69 +13007,19 @@ } }, "@typescript-eslint/experimental-utils": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.0.tgz", - "integrity": "sha512-9XD9s7mt3QWMk82GoyUpc/Ji03vz4T5AYlHF9DcoFNfJ/y3UAclRsfGiE2gLfXtyC+JRA3trR7cR296TEb1oiQ==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.1.tgz", + "integrity": "sha512-n8/ggadrZ+uyrfrSEchx3jgODdmcx7MzVM2sI3cTpI/YlfSm0+9HEUaWw3aQn2urL2KYlWYMDgn45iLfjDYB+Q==", "dev": true, "requires": { "@types/json-schema": "^7.0.7", - "@typescript-eslint/scope-manager": "4.28.0", - "@typescript-eslint/types": "4.28.0", - "@typescript-eslint/typescript-estree": "4.28.0", + "@typescript-eslint/scope-manager": "4.28.1", + "@typescript-eslint/types": "4.28.1", + "@typescript-eslint/typescript-estree": "4.28.1", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, "dependencies": { - "@typescript-eslint/scope-manager": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.0.tgz", - "integrity": "sha512-eCALCeScs5P/EYjwo6se9bdjtrh8ByWjtHzOkC4Tia6QQWtQr3PHovxh3TdYTuFcurkYI4rmFsRFpucADIkseg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.28.0", - "@typescript-eslint/visitor-keys": "4.28.0" - } - }, - "@typescript-eslint/types": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.0.tgz", - "integrity": "sha512-p16xMNKKoiJCVZY5PW/AfILw2xe1LfruTcfAKBj3a+wgNYP5I9ZEKNDOItoRt53p4EiPV6iRSICy8EPanG9ZVA==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.0.tgz", - "integrity": "sha512-m19UQTRtxMzKAm8QxfKpvh6OwQSXaW1CdZPoCaQuLwAq7VZMNuhJmZR4g5281s2ECt658sldnJfdpSZZaxUGMQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.28.0", - "@typescript-eslint/visitor-keys": "4.28.0", - "debug": "^4.3.1", - "globby": "^11.0.3", - "is-glob": "^4.0.1", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.0.tgz", - "integrity": "sha512-PjJyTWwrlrvM5jazxYF5ZPs/nl0kHDZMVbuIcbpawVXaDPelp3+S9zpOz5RmVUfS/fD5l5+ZXNKnWhNYjPzCvw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.28.0", - "eslint-visitor-keys": "^2.0.0" - } - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, "eslint-utils": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", @@ -13322,59 +13034,18 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", "dev": true - }, - "globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - } - }, - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true } } }, "@typescript-eslint/parser": { - "version": "4.27.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.27.0.tgz", - "integrity": "sha512-XpbxL+M+gClmJcJ5kHnUpBGmlGdgNvy6cehgR6ufyxkEJMGP25tZKCaKyC0W/JVpuhU3VU1RBn7SYUPKSMqQvQ==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.28.1.tgz", + "integrity": "sha512-UjrMsgnhQIIK82hXGaD+MCN8IfORS1CbMdu7VlZbYa8LCZtbZjJA26De4IPQB7XYZbL8gJ99KWNj0l6WD0guJg==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "4.27.0", - "@typescript-eslint/types": "4.27.0", - "@typescript-eslint/typescript-estree": "4.27.0", + "@typescript-eslint/scope-manager": "4.28.1", + "@typescript-eslint/types": "4.28.1", + "@typescript-eslint/typescript-estree": "4.28.1", "debug": "^4.3.1" }, "dependencies": { @@ -13396,29 +13067,29 @@ } }, "@typescript-eslint/scope-manager": { - "version": "4.27.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.27.0.tgz", - "integrity": "sha512-DY73jK6SEH6UDdzc6maF19AHQJBFVRf6fgAXHPXCGEmpqD4vYgPEzqpFz1lf/daSbOcMpPPj9tyXXDPW2XReAw==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.1.tgz", + "integrity": "sha512-o95bvGKfss6705x7jFGDyS7trAORTy57lwJ+VsYwil/lOUxKQ9tA7Suuq+ciMhJc/1qPwB3XE2DKh9wubW8YYA==", "dev": true, "requires": { - "@typescript-eslint/types": "4.27.0", - "@typescript-eslint/visitor-keys": "4.27.0" + "@typescript-eslint/types": "4.28.1", + "@typescript-eslint/visitor-keys": "4.28.1" } }, "@typescript-eslint/types": { - "version": "4.27.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.27.0.tgz", - "integrity": "sha512-I4ps3SCPFCKclRcvnsVA/7sWzh7naaM/b4pBO2hVxnM3wrU51Lveybdw5WoIktU/V4KfXrTt94V9b065b/0+wA==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.1.tgz", + "integrity": "sha512-4z+knEihcyX7blAGi7O3Fm3O6YRCP+r56NJFMNGsmtdw+NCdpG5SgNz427LS9nQkRVTswZLhz484hakQwB8RRg==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "4.27.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.27.0.tgz", - "integrity": "sha512-KH03GUsUj41sRLLEy2JHstnezgpS5VNhrJouRdmh6yNdQ+yl8w5LrSwBkExM+jWwCJa7Ct2c8yl8NdtNRyQO6g==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.1.tgz", + "integrity": "sha512-GhKxmC4sHXxHGJv8e8egAZeTZ6HI4mLU6S7FUzvFOtsk7ZIDN1ksA9r9DyOgNqowA9yAtZXV0Uiap61bIO81FQ==", "dev": true, "requires": { - "@typescript-eslint/types": "4.27.0", - "@typescript-eslint/visitor-keys": "4.27.0", + "@typescript-eslint/types": "4.28.1", + "@typescript-eslint/visitor-keys": "4.28.1", "debug": "^4.3.1", "globby": "^11.0.3", "is-glob": "^4.0.1", @@ -13436,9 +13107,9 @@ } }, "globby": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", - "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", "dev": true, "requires": { "array-union": "^2.1.0", @@ -13479,12 +13150,12 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "4.27.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.27.0.tgz", - "integrity": "sha512-es0GRYNZp0ieckZ938cEANfEhsfHrzuLrePukLKtY3/KPXcq1Xd555Mno9/GOgXhKzn0QfkDLVgqWO3dGY80bg==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.1.tgz", + "integrity": "sha512-K4HMrdFqr9PFquPu178SaSb92CaWe2yErXyPumc8cYWxFmhgJsNY9eSePmO05j0JhBvf2Cdhptd6E6Yv9HVHcg==", "dev": true, "requires": { - "@typescript-eslint/types": "4.27.0", + "@typescript-eslint/types": "4.28.1", "eslint-visitor-keys": "^2.0.0" }, "dependencies": { @@ -14572,15 +14243,15 @@ "optional": true }, "core-js": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.14.0.tgz", - "integrity": "sha512-3s+ed8er9ahK+zJpp9ZtuVcDoFzHNiZsPbNAAE4KXgrRHbjSqqNN6xGSXq6bq7TZIbKj4NLrLb6bJ5i+vSVjHA==", + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.15.1.tgz", + "integrity": "sha512-h8VbZYnc9pDzueiS2610IULDkpFFPunHwIpl8yRwFahAEEdSpHlTy3h3z3rKq5h11CaUdBEeRViu9AYvbxiMeg==", "dev": true }, "core-js-compat": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.14.0.tgz", - "integrity": "sha512-R4NS2eupxtiJU+VwgkF9WTpnSfZW4pogwKHd8bclWU2sp93Pr5S1uYJI84cMOubJRou7bcfL0vmwtLslWN5p3A==", + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.15.1.tgz", + "integrity": "sha512-xGhzYMX6y7oEGQGAJmP2TmtBLvR4nZmRGEcFa3ubHOq5YEp51gGN9AovVa0AoujGZIq+Wm6dISiYyGNfdflYww==", "dev": true, "requires": { "browserslist": "^4.16.6", @@ -14934,9 +14605,9 @@ "dev": true }, "eslint": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.28.0.tgz", - "integrity": "sha512-UMfH0VSjP0G4p3EWirscJEQ/cHqnT/iuH6oNZOB94nBjWbMnhGEPxsZm1eyIW0C/9jLI0Fow4W5DXLjEI7mn1g==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.29.0.tgz", + "integrity": "sha512-82G/JToB9qIy/ArBzIWG9xvvwL3R86AlCjtGw+A29OMZDqhTybz/MByORSukGxeI+YPCR4coYyITKk8BFH9nDA==", "dev": true, "requires": { "@babel/code-frame": "7.12.11", @@ -15222,9 +14893,9 @@ } }, "eslint-plugin-flowtype": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-5.7.2.tgz", - "integrity": "sha512-7Oq/N0+3nijBnYWQYzz/Mp/7ZCpwxYvClRyW/PLAmimY9uLCBvoXsNsERcJdkKceyOjgRbFhhxs058KTrne9Mg==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-5.8.0.tgz", + "integrity": "sha512-feK1xnUTsMSNTOw9jFw7aVgZl7Ep+ghpta/YEoaV6jbXU6Yso30B7BIj9ObHLzZ5TFJL7D98az080wfykLCrcw==", "dev": true, "requires": { "lodash": "^4.17.15", @@ -15829,18 +15500,18 @@ "dev": true }, "flow-parser": { - "version": "0.153.0", - "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.153.0.tgz", - "integrity": "sha512-qa7UODgbofQyruRWqNQ+KM5hO37CenByxhNfAztiwjBsPhWZ5AFh5g+gtLpTWPlzG7X66QdjBB9DuHNUBcaF+Q==", + "version": "0.154.0", + "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.154.0.tgz", + "integrity": "sha512-cH9xY/ljOgmqG1n7PU1jffiHhRggoloauwOrOlCWBEX4Y+ml6GA8g//tCVKU+6PO4BXoPF22TFHkS5E1bN3JOQ==", "dev": true }, "flow-remove-types": { - "version": "2.153.0", - "resolved": "https://registry.npmjs.org/flow-remove-types/-/flow-remove-types-2.153.0.tgz", - "integrity": "sha512-KHl0r0xH5VBMOtKmk2WJME3F4UDg+j0f/yH6lbBIGk3xKl5NTHLs+1h9murH9+KLxvVZ0ut5ydd/fx49RSqmEw==", + "version": "2.154.0", + "resolved": "https://registry.npmjs.org/flow-remove-types/-/flow-remove-types-2.154.0.tgz", + "integrity": "sha512-i55LkOLp4JRYrw11xiVx/GzY9IvnJORTEw6oKcoBKTQu8iULc+rnI4YlMSZpcki00Y3EOA/xk9XXLYt4obtfug==", "dev": true, "requires": { - "flow-parser": "^0.153.0", + "flow-parser": "^0.154.0", "pirates": "^3.0.2", "vlq": "^0.2.1" }, @@ -18087,9 +17758,9 @@ "dev": true }, "prettier": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.1.tgz", - "integrity": "sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", + "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==", "dev": true }, "process-nextick-args": { @@ -18455,9 +18126,9 @@ } }, "rollup": { - "version": "2.52.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.0.tgz", - "integrity": "sha512-lSkBDGsVoXjqaBf7dsHwxBJz+p+hJEP72P+LOitA0yVs+Nzxj76FidkZE2thrmhjwGqLYiJo39opi7mAfaQ/Vg==", + "version": "2.52.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.4.tgz", + "integrity": "sha512-AXgUxxWXyGfsj8GKleR1k8KsG8G+7ZZDRU9RZb9PnLGSyTqI/1qf/+QSp1hRaR40j4yfBCKXs5khtGKiFwihfg==", "dev": true, "requires": { "fsevents": "~2.3.2" @@ -19543,9 +19214,9 @@ "dev": true }, "typescript": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.2.tgz", - "integrity": "sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.4.tgz", + "integrity": "sha512-uauPG7XZn9F/mo+7MrsRjyvbxFpzemRjKEZXS4AK83oP2KKOJPvb+9cO/gmnv8arWZvhnjVOXz7B49m1l0e9Ew==", "dev": true }, "typescript-compiler": {