Skip to content

Commit

Permalink
Merge pull request #60 from Squads-Protocol/feat/rent-reclamation
Browse files Browse the repository at this point in the history
Rent Reclamation
  • Loading branch information
vovacodes authored Dec 2, 2023
2 parents 4df1f87 + 20a1df8 commit e35bf30
Show file tree
Hide file tree
Showing 63 changed files with 6,827 additions and 188 deletions.
7 changes: 3 additions & 4 deletions Anchor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,9 @@ wallet = "~/.config/solana/id.json"
[test.validator]
url = "https://api.devnet.solana.com"

# Token2022
[[test.validator.clone]]
address = "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"

[[test.validator.account]]
address = "D3oQ6QxSYk6aKUsmBTa9BghFQvbRi7kxP6h95NSdjjXz"
filename = "tests/fixtures/pre-rent-collector/multisig-account.json"

[scripts]
test = "npx mocha --node-option require=ts-node/register --extension ts -t 1000000 tests/index.ts"
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
},
"devDependencies": {
"@solana/spl-token": "*",
"@solana/spl-memo": "^0.2.3",
"@types/bn.js": "5.1.0",
"@types/mocha": "10.0.1",
"@types/node-fetch": "2.6.2",
Expand Down
10 changes: 10 additions & 0 deletions programs/squads_multisig_program/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,14 @@ pub enum MultisigError {
TimeLockExceedsMaxAllowed,
#[msg("Account is not owned by Multisig program")]
IllegalAccountOwner,
#[msg("Rent reclamation is disabled for this multisig")]
RentReclamationDisabled,
#[msg("Invalid rent collector address")]
InvalidRentCollector,
#[msg("Proposal is for another multisig")]
ProposalForAnotherMultisig,
#[msg("Transaction is for another multisig")]
TransactionForAnotherMultisig,
#[msg("Transaction doesn't match proposal")]
TransactionNotMatchingProposal,
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ impl BatchCreate<'_> {
batch.size = 0;
batch.executed_transaction_index = 0;

batch.invariant()?;

// Updated last transaction index in the multisig account.
multisig.transaction_index = index;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,32 @@ impl<'info> ConfigTransactionExecute<'info> {
let rent = Rent::get()?;

// Check applying the config actions will require reallocation of space for the multisig account.

// Handle growing members vector.
let new_members_length =
members_length_after_actions(multisig.members.len(), &transaction.actions);
if new_members_length > multisig.members.len() {
let needs_members_allocation = new_members_length > multisig.members.len();

// Handle growing rent_collector changing from None -> Some(Pubkey).
// The `rent_collector` field after applying the actions will be:
// - the `new_rent_collector` of the last `SetRentCollector` action, if any present among the actions.
// - the current `rent_collector` if no `SetRentCollector` action is present among the actions.
let new_rent_collector = transaction
.actions
.iter()
.rev()
.find_map(|action| {
if let ConfigAction::SetRentCollector { new_rent_collector } = action {
Some(*new_rent_collector)
} else {
None
}
})
.unwrap_or(multisig.rent_collector);
let needs_rent_collector_allocation =
multisig.rent_collector.is_none() && new_rent_collector.is_some();

if needs_members_allocation || needs_rent_collector_allocation {
let rent_payer = &ctx
.accounts
.rent_payer
Expand All @@ -126,6 +149,7 @@ impl<'info> ConfigTransactionExecute<'info> {
let reallocated = Multisig::realloc_if_needed(
multisig.to_account_info(),
new_members_length,
new_rent_collector.is_some(),
rent_payer.to_account_info(),
system_program.to_account_info(),
)?;
Expand Down Expand Up @@ -280,6 +304,13 @@ impl<'info> ConfigTransactionExecute<'info> {
// We don't need to invalidate prior transactions here because adding
// a spending limit doesn't affect the consensus parameters of the multisig.
}

ConfigAction::SetRentCollector { new_rent_collector } => {
multisig.rent_collector = *new_rent_collector;

// We don't need to invalidate prior transactions here because changing
// `rent_collector` doesn't affect the consensus parameters of the multisig.
}
}
}

Expand Down Expand Up @@ -312,6 +343,7 @@ fn members_length_after_actions(members_length: usize, actions: &[ConfigAction])
ConfigAction::SetTimeLock { .. } => acc,
ConfigAction::AddSpendingLimit { .. } => acc,
ConfigAction::RemoveSpendingLimit { .. } => acc,
ConfigAction::SetRentCollector { .. } => acc,
});

let abs_members_delta =
Expand Down
2 changes: 2 additions & 0 deletions programs/squads_multisig_program/src/instructions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub use proposal_activate::*;
pub use proposal_create::*;
pub use proposal_vote::*;
pub use spending_limit_use::*;
pub use transaction_accounts_close::*;
pub use vault_transaction_create::*;
pub use vault_transaction_execute::*;

Expand All @@ -27,5 +28,6 @@ mod proposal_activate;
mod proposal_create;
mod proposal_vote;
mod spending_limit_use;
mod transaction_accounts_close;
mod vault_transaction_create;
mod vault_transaction_execute;
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ pub struct MultisigSetConfigAuthorityArgs {
pub memo: Option<String>,
}

#[derive(AnchorSerialize, AnchorDeserialize)]
pub struct MultisigSetRentCollectorArgs {
pub rent_collector: Option<Pubkey>,
/// Memo is used for indexing only.
pub memo: Option<String>,
}

#[derive(Accounts)]
pub struct MultisigConfig<'info> {
#[account(
Expand Down Expand Up @@ -95,6 +102,7 @@ impl MultisigConfig<'_> {
let reallocated = Multisig::realloc_if_needed(
multisig.to_account_info(),
multisig.members.len() + 1,
multisig.rent_collector.is_some(),
rent_payer.to_account_info(),
system_program.to_account_info(),
)?;
Expand Down Expand Up @@ -199,4 +207,49 @@ impl MultisigConfig<'_> {

Ok(())
}

/// Set the multisig `rent_collector` and reallocate space if necessary.
///
/// NOTE: This instruction must be called only by the `config_authority` if one is set (Controlled Multisig).
/// Uncontrolled Mustisigs should use `config_transaction_create` instead.
#[access_control(ctx.accounts.validate())]
pub fn multisig_set_rent_collector(
ctx: Context<Self>,
args: MultisigSetRentCollectorArgs,
) -> Result<()> {
let multisig = &mut ctx.accounts.multisig;

let system_program = &ctx
.accounts
.system_program
.as_ref()
.ok_or(MultisigError::MissingAccount)?;
let rent_payer = &ctx
.accounts
.rent_payer
.as_ref()
.ok_or(MultisigError::MissingAccount)?;

// Check if we need to reallocate space.
let reallocated = Multisig::realloc_if_needed(
multisig.to_account_info(),
multisig.members.len(),
args.rent_collector.is_some(),
rent_payer.to_account_info(),
system_program.to_account_info(),
)?;

if reallocated {
multisig.reload()?;
}

multisig.rent_collector = args.rent_collector;

// We don't need to invalidate prior transactions here because changing
// `rent_collector` doesn't affect the consensus parameters of the multisig.

multisig.invariant()?;

Ok(())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ pub struct MultisigCreateArgs {
pub threshold: u16,
/// The members of the multisig.
pub members: Vec<Member>,
/// How many seconds must pass between transaction voting settlement and execution.
/// How many seconds must pass between transaction voting, settlement, and execution.
pub time_lock: u32,
/// The address where the rent for the accounts related to executed, rejected, or cancelled
/// transactions can be reclaimed. If set to `None`, the rent reclamation feature is turned off.
pub rent_collector: Option<Pubkey>,
/// Memo is used for indexing only.
pub memo: Option<String>,
}
Expand All @@ -23,7 +26,7 @@ pub struct MultisigCreate<'info> {
#[account(
init,
payer = creator,
space = Multisig::size(args.members.len()),
space = Multisig::size(args.members.len(), args.rent_collector.is_some()),
seeds = [SEED_PREFIX, SEED_MULTISIG, create_key.key().as_ref()],
bump
)]
Expand Down Expand Up @@ -62,6 +65,7 @@ impl MultisigCreate<'_> {
multisig.create_key = ctx.accounts.create_key.key();
multisig.bump = ctx.bumps.multisig;
multisig.members = members;
multisig.rent_collector = args.rent_collector;

multisig.invariant()?;

Expand Down
Loading

0 comments on commit e35bf30

Please sign in to comment.