-
Notifications
You must be signed in to change notification settings - Fork 44
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(SpendingLimits): add/remove spending limit for controlled multisig
- Loading branch information
Showing
19 changed files
with
895 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
103 changes: 103 additions & 0 deletions
103
programs/multisig/src/instructions/multisig_add_spending_limit.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
use anchor_lang::prelude::*; | ||
|
||
use crate::errors::*; | ||
use crate::state::*; | ||
|
||
#[derive(AnchorSerialize, AnchorDeserialize)] | ||
pub struct MultisigAddSpendingLimitArgs { | ||
/// Key that is used to seed the SpendingLimit PDA. | ||
pub create_key: Pubkey, | ||
/// The index of the vault that the spending limit is for. | ||
pub vault_index: u8, | ||
/// The token mint the spending limit is for. | ||
pub mint: Pubkey, | ||
/// The amount of tokens that can be spent in a period. | ||
/// This amount is in decimals of the mint, | ||
/// so 1 SOL would be `1_000_000_000` and 1 USDC would be `1_000_000`. | ||
pub amount: u64, | ||
/// The reset period of the spending limit. | ||
/// When it passes, the remaining amount is reset, unless it's `Period::OneTime`. | ||
pub period: Period, | ||
/// Members of the multisig that can use the spending limit. | ||
/// In case a member is removed from the multisig, the spending limit will remain existent | ||
/// (until explicitly deleted), but the removed member will not be able to use it anymore. | ||
pub members: Vec<Pubkey>, | ||
/// The destination addresses the spending limit is allowed to sent funds to. | ||
/// If empty, funds can be sent to any address. | ||
pub destinations: Vec<Pubkey>, | ||
/// Memo is used for indexing only. | ||
pub memo: Option<String>, | ||
} | ||
|
||
#[derive(Accounts)] | ||
#[instruction(args: MultisigAddSpendingLimitArgs)] | ||
pub struct MultisigAddSpendingLimit<'info> { | ||
#[account( | ||
seeds = [SEED_PREFIX, SEED_MULTISIG, multisig.create_key.as_ref()], | ||
bump = multisig.bump, | ||
)] | ||
multisig: Account<'info, Multisig>, | ||
|
||
/// Multisig `config_authority` that must authorize the configuration change. | ||
pub config_authority: Signer<'info>, | ||
|
||
#[account( | ||
init, | ||
seeds = [ | ||
SEED_PREFIX, | ||
multisig.key().as_ref(), | ||
SEED_SPENDING_LIMIT, | ||
args.create_key.as_ref(), | ||
], | ||
bump, | ||
space = SpendingLimit::size(args.members.len(), args.destinations.len()), | ||
payer = rent_payer | ||
)] | ||
pub spending_limit: Account<'info, SpendingLimit>, | ||
|
||
/// This is usually the same as `config_authority`, but can be a different account if needed. | ||
#[account(mut)] | ||
pub rent_payer: Signer<'info>, | ||
|
||
pub system_program: Program<'info, System>, | ||
} | ||
|
||
impl MultisigAddSpendingLimit<'_> { | ||
fn validate(&self) -> Result<()> { | ||
// config_authority | ||
require_keys_eq!( | ||
self.config_authority.key(), | ||
self.multisig.config_authority, | ||
MultisigError::Unauthorized | ||
); | ||
|
||
// `spending_limit` is checked via its seeds. | ||
|
||
Ok(()) | ||
} | ||
|
||
/// Create a new spending limit for the controlled multisig. | ||
/// 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_add_spending_limit( | ||
ctx: Context<Self>, | ||
args: MultisigAddSpendingLimitArgs, | ||
) -> Result<()> { | ||
let spending_limit = &mut ctx.accounts.spending_limit; | ||
|
||
spending_limit.multisig = ctx.accounts.multisig.key(); | ||
spending_limit.create_key = args.create_key; | ||
spending_limit.vault_index = args.vault_index; | ||
spending_limit.mint = args.mint; | ||
spending_limit.amount = args.amount; | ||
spending_limit.period = args.period; | ||
spending_limit.remaining_amount = args.amount; | ||
spending_limit.last_reset = Clock::get()?.unix_timestamp; | ||
spending_limit.bump = *ctx.bumps.get("spending_limit").unwrap(); | ||
spending_limit.members = args.members; | ||
spending_limit.destinations = args.destinations; | ||
|
||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
61 changes: 61 additions & 0 deletions
61
programs/multisig/src/instructions/multisig_remove_spending_limit.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
use anchor_lang::prelude::*; | ||
|
||
use crate::errors::*; | ||
use crate::state::*; | ||
|
||
#[derive(AnchorSerialize, AnchorDeserialize)] | ||
pub struct MultisigRemoveSpendingLimitArgs { | ||
/// Memo is used for indexing only. | ||
pub memo: Option<String>, | ||
} | ||
|
||
#[derive(Accounts)] | ||
pub struct MultisigRemoveSpendingLimit<'info> { | ||
#[account( | ||
seeds = [SEED_PREFIX, SEED_MULTISIG, multisig.create_key.as_ref()], | ||
bump = multisig.bump, | ||
)] | ||
multisig: Account<'info, Multisig>, | ||
|
||
/// Multisig `config_authority` that must authorize the configuration change. | ||
pub config_authority: Signer<'info>, | ||
|
||
#[account(mut, close = rent_collector)] | ||
pub spending_limit: Account<'info, SpendingLimit>, | ||
|
||
/// This is usually the same as `config_authority`, but can be a different account if needed. | ||
/// CHECK: can be any account. | ||
#[account(mut)] | ||
pub rent_collector: AccountInfo<'info>, | ||
} | ||
|
||
impl MultisigRemoveSpendingLimit<'_> { | ||
fn validate(&self) -> Result<()> { | ||
// config_authority | ||
require_keys_eq!( | ||
self.config_authority.key(), | ||
self.multisig.config_authority, | ||
MultisigError::Unauthorized | ||
); | ||
|
||
// `spending_limit` | ||
require_keys_eq!( | ||
self.spending_limit.multisig, | ||
self.multisig.key(), | ||
MultisigError::InvalidAccount | ||
); | ||
|
||
Ok(()) | ||
} | ||
|
||
/// Remove the spending limit from the controlled multisig. | ||
/// 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_remove_spending_limit( | ||
ctx: Context<Self>, | ||
_args: MultisigRemoveSpendingLimitArgs, | ||
) -> Result<()> { | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.