@@ -8,7 +8,21 @@ use crate::state::*;
88#[ derive( AnchorSerialize , AnchorDeserialize ) ]
99pub struct MultisigAddMemberArgs {
1010 new_member : Member ,
11- /// Memo isn't used for anything, but is included in `CreatedEvent` that can later be parsed and indexed.
11+ /// Memo isn't used for anything, but is included in `AddMemberEvent` that can later be parsed and indexed.
12+ pub memo : Option < String > ,
13+ }
14+
15+ #[ derive( AnchorSerialize , AnchorDeserialize ) ]
16+ pub struct MultisigRemoveMemberArgs {
17+ old_member : Member ,
18+ /// Memo isn't used for anything, but is included in `RemoveMemberEvent` that can later be parsed and indexed.
19+ pub memo : Option < String > ,
20+ }
21+
22+ #[ derive( AnchorSerialize , AnchorDeserialize ) ]
23+ pub struct MultisigChangeThresholdArgs {
24+ new_threshold : u16 ,
25+ /// Memo isn't used for anything, but is included in `ChangeThreshold` that can later be parsed and indexed.
1226 pub memo : Option < String > ,
1327}
1428
@@ -18,11 +32,13 @@ pub struct MultisigConfig<'info> {
1832 mut ,
1933 seeds = [ SEED_PREFIX , multisig. create_key. as_ref( ) , SEED_MULTISIG ] ,
2034 bump = multisig. bump,
21- has_one = config_authority @ MultisigError :: Unauthorized ,
2235 ) ]
2336 multisig : Account < ' info , Multisig > ,
2437
25- #[ account( mut ) ]
38+ #[ account(
39+ mut ,
40+ constraint = config_authority. key( ) == multisig. config_authority @ MultisigError :: Unauthorized ,
41+ ) ]
2642 pub config_authority : Signer < ' info > ,
2743
2844 /// We might need it in case reallocation is needed.
@@ -32,28 +48,19 @@ pub struct MultisigConfig<'info> {
3248impl MultisigConfig < ' _ > {
3349 /// Add a member/key to the multisig and reallocate space if necessary.
3450 pub fn multisig_add_member ( ctx : Context < Self > , args : MultisigAddMemberArgs ) -> Result < ( ) > {
35- let multisig = & mut ctx. accounts . multisig ;
36-
3751 let MultisigAddMemberArgs { new_member, memo } = args;
3852
39- require ! (
40- multisig. is_member( new_member. key) . is_none( ) ,
41- MultisigError :: MemberAlreadyExists
42- ) ;
53+ let multisig = & mut ctx. accounts . multisig ;
54+ let multisig_key = multisig. to_account_info ( ) . key ( ) ;
4355
4456 let current_members_length = multisig. members . len ( ) ;
45- // If max is already reached, we can't have more members.
46- require ! (
47- current_members_length < usize :: from( u16 :: MAX ) ,
48- MultisigError :: MaxMembersReached
49- ) ;
50-
5157 let current_account_size = multisig. to_account_info ( ) . data . borrow ( ) . len ( ) ;
58+
5259 // Check if we need to reallocate space.
5360 let reallocated = if current_account_size < Multisig :: size ( current_members_length + 1 ) {
5461 // We need to allocate more space. To avoid doing this operation too often, we increment it by 10 members.
5562 let new_size = current_account_size + ( 10 * Member :: size ( ) ) ;
56- // Reallocate more space
63+ // Reallocate more space.
5764 AccountInfo :: realloc ( & multisig. to_account_info ( ) , new_size, false ) ?;
5865
5966 // If more lamports are needed, transfer them to the account
@@ -78,9 +85,10 @@ impl MultisigConfig<'_> {
7885 false
7986 } ;
8087
81- multisig. add_member_if_not_exists ( new_member) ;
88+ multisig. add_member ( new_member) ;
89+
90+ multisig. invariant ( ) ?;
8291
83- let multisig_key = multisig. to_account_info ( ) . key ( ) ;
8492 multisig. config_updated (
8593 multisig_key,
8694 ConfigUpdateType :: AddMember { reallocated } ,
@@ -89,4 +97,58 @@ impl MultisigConfig<'_> {
8997
9098 Ok ( ( ) )
9199 }
100+
101+ /// Remove a member/key from the multisig.
102+ pub fn multisig_remove_member (
103+ ctx : Context < Self > ,
104+ args : MultisigRemoveMemberArgs ,
105+ ) -> Result < ( ) > {
106+ let multisig = & mut ctx. accounts . multisig ;
107+ let multisig_key = multisig. to_account_info ( ) . key ( ) ;
108+
109+ require ! ( multisig. members. len( ) > 1 , MultisigError :: RemoveLastMember ) ;
110+
111+ let old_member_index = match multisig. is_member ( args. old_member . key ) {
112+ Some ( old_member_index) => old_member_index,
113+ None => return err ! ( MultisigError :: NotAMember ) ,
114+ } ;
115+
116+ multisig. members . remove ( old_member_index) ;
117+
118+ // Update the threshold if necessary.
119+ if usize:: from ( multisig. threshold ) > multisig. members . len ( ) {
120+ multisig. threshold = multisig
121+ . members
122+ . len ( )
123+ . try_into ( )
124+ . expect ( "didn't expect more that `u16::MAX` members" ) ;
125+ }
126+
127+ multisig. invariant ( ) ?;
128+
129+ multisig. config_updated ( multisig_key, ConfigUpdateType :: RemoveMember , args. memo ) ;
130+
131+ Ok ( ( ) )
132+ }
133+
134+ pub fn multisig_change_threshold (
135+ ctx : Context < Self > ,
136+ args : MultisigChangeThresholdArgs ,
137+ ) -> Result < ( ) > {
138+ let MultisigChangeThresholdArgs {
139+ new_threshold,
140+ memo,
141+ } = args;
142+
143+ let multisig = & mut ctx. accounts . multisig ;
144+ let multisig_key = multisig. to_account_info ( ) . key ( ) ;
145+
146+ multisig. threshold = new_threshold;
147+
148+ multisig. invariant ( ) ?;
149+
150+ multisig. config_updated ( multisig_key, ConfigUpdateType :: ChangeThreshold , memo) ;
151+
152+ Ok ( ( ) )
153+ }
92154}
0 commit comments