Skip to content

Commit e0229a4

Browse files
committed
feat(multisig_add_vault): implement ix, sdk and tests
1 parent 5f98a23 commit e0229a4

File tree

18 files changed

+539
-4
lines changed

18 files changed

+539
-4
lines changed

programs/multisig/src/errors.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,6 @@ pub enum MultisigError {
4848
NoActions,
4949
#[msg("Missing account")]
5050
MissingAccount,
51+
#[msg("Invalid `vault_index`")]
52+
InvalidVaultIndex,
5153
}

programs/multisig/src/instructions/multisig_config.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,16 @@ pub struct MultisigSetConfigAuthorityArgs {
3838
pub memo: Option<String>,
3939
}
4040

41+
#[derive(AnchorSerialize, AnchorDeserialize)]
42+
pub struct MultisigAddVaultArgs {
43+
/// The next vault index to set as the latest used.
44+
/// Must be the current `vault_index + 1`.
45+
/// We pass it explicitly to make this instruction idempotent.
46+
vault_index: u8,
47+
/// Memo isn't used for anything, but is included in `ChangeThreshold` that can later be parsed and indexed.
48+
pub memo: Option<String>,
49+
}
50+
4151
#[derive(Accounts)]
4252
pub struct MultisigConfig<'info> {
4353
#[account(
@@ -72,6 +82,9 @@ impl MultisigConfig<'_> {
7282
}
7383

7484
/// Add a member/key to the multisig and reallocate space if necessary.
85+
///
86+
/// NOTE: This instruction must be called only by the `config_authority` if one is set (Controlled Multisig).
87+
/// Uncontrolled Mustisigs should use `config_transaction_create` instead.
7588
#[access_control(ctx.accounts.validate())]
7689
pub fn multisig_add_member(ctx: Context<Self>, args: MultisigAddMemberArgs) -> Result<()> {
7790
let MultisigAddMemberArgs { new_member, .. } = args;
@@ -110,6 +123,9 @@ impl MultisigConfig<'_> {
110123
}
111124

112125
/// Remove a member/key from the multisig.
126+
///
127+
/// NOTE: This instruction must be called only by the `config_authority` if one is set (Controlled Multisig).
128+
/// Uncontrolled Mustisigs should use `config_transaction_create` instead.
113129
#[access_control(ctx.accounts.validate())]
114130
pub fn multisig_remove_member(
115131
ctx: Context<Self>,
@@ -137,6 +153,8 @@ impl MultisigConfig<'_> {
137153
Ok(())
138154
}
139155

156+
/// NOTE: This instruction must be called only by the `config_authority` if one is set (Controlled Multisig).
157+
/// Uncontrolled Mustisigs should use `config_transaction_create` instead.
140158
#[access_control(ctx.accounts.validate())]
141159
pub fn multisig_change_threshold(
142160
ctx: Context<Self>,
@@ -156,6 +174,9 @@ impl MultisigConfig<'_> {
156174
}
157175

158176
/// Set the `time_lock` config parameter for the multisig.
177+
///
178+
/// NOTE: This instruction must be called only by the `config_authority` if one is set (Controlled Multisig).
179+
/// Uncontrolled Mustisigs should use `config_transaction_create` instead.
159180
#[access_control(ctx.accounts.validate())]
160181
pub fn multisig_set_time_lock(ctx: Context<Self>, args: MultisigSetTimeLockArgs) -> Result<()> {
161182
let multisig = &mut ctx.accounts.multisig;
@@ -170,6 +191,9 @@ impl MultisigConfig<'_> {
170191
}
171192

172193
/// Set the multisig `config_authority`.
194+
///
195+
/// NOTE: This instruction must be called only by the `config_authority` if one is set (Controlled Multisig).
196+
/// Uncontrolled Mustisigs should use `config_transaction_create` instead.
173197
#[access_control(ctx.accounts.validate())]
174198
pub fn multisig_set_config_authority(
175199
ctx: Context<Self>,
@@ -185,4 +209,29 @@ impl MultisigConfig<'_> {
185209

186210
Ok(())
187211
}
212+
213+
/// Increment the multisig `vault_index`.
214+
/// This doesn't actually "add" a new vault, because vaults are derived from the multisig address and index, so technically
215+
/// they always exist. This just increments the index so that UIs can show the "used" vaults.
216+
///
217+
/// NOTE: This instruction must be called only by the `config_authority` if one is set (Controlled Multisig).
218+
/// Uncontrolled Mustisigs should use `config_transaction_create` instead.
219+
#[access_control(ctx.accounts.validate())]
220+
pub fn multisig_add_vault(ctx: Context<Self>, args: MultisigAddVaultArgs) -> Result<()> {
221+
let multisig = &mut ctx.accounts.multisig;
222+
223+
require!(
224+
args.vault_index == multisig.vault_index + 1,
225+
MultisigError::InvalidVaultIndex
226+
);
227+
228+
multisig.vault_index = args.vault_index;
229+
230+
multisig.invariant()?;
231+
232+
// NOTE: we don't call `multisig.config_updated` here, because this doesn't
233+
// affect the transactions by any means, and really just a UI feature.
234+
235+
Ok(())
236+
}
188237
}

programs/multisig/src/lib.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,19 @@ pub mod multisig {
5858
MultisigConfig::multisig_set_config_authority(ctx, args)
5959
}
6060

61+
/// Increment the multisig `vault_index`.
62+
/// This doesn't actually "add" a new vault, because vaults are derived from the multisig address and index, so technically
63+
/// they always exist. This just increments the index so that UIs can show the "used" vaults.
64+
///
65+
/// NOTE: This instruction must be called only by the `config_authority` if one is set (Controlled Multisig).
66+
/// Uncontrolled Mustisigs should use `config_transaction_create` instead.
67+
pub fn multisig_add_vault(
68+
ctx: Context<MultisigConfig>,
69+
args: MultisigAddVaultArgs,
70+
) -> Result<()> {
71+
MultisigConfig::multisig_add_vault(ctx, args)
72+
}
73+
6174
/// Create a new config transaction.
6275
pub fn config_transaction_create(
6376
ctx: Context<ConfigTransactionCreate>,

sdk/multisig/idl/multisig.json

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,60 @@
241241
}
242242
]
243243
},
244+
{
245+
"name": "multisigAddVault",
246+
"docs": [
247+
"Increment the multisig `vault_index`.",
248+
"This doesn't actually \"add\" a new vault, because vaults are derived from the multisig address and index, so technically",
249+
"they always exist. This just increments the index so that UIs can show the \"used\" vaults.",
250+
"",
251+
"NOTE: This instruction must be called only by the `config_authority` if one is set (Controlled Multisig).",
252+
"Uncontrolled Mustisigs should use `config_transaction_create` instead."
253+
],
254+
"accounts": [
255+
{
256+
"name": "multisig",
257+
"isMut": true,
258+
"isSigner": false
259+
},
260+
{
261+
"name": "configAuthority",
262+
"isMut": false,
263+
"isSigner": true,
264+
"docs": [
265+
"Multisig `config_authority` that must authorize the configuration change."
266+
]
267+
},
268+
{
269+
"name": "rentPayer",
270+
"isMut": true,
271+
"isSigner": true,
272+
"isOptional": true,
273+
"docs": [
274+
"The account that will be charged in case the multisig account needs to reallocate space,",
275+
"for example when adding a new member.",
276+
"This is usually the same as `config_authority`, but can be a different account if needed."
277+
]
278+
},
279+
{
280+
"name": "systemProgram",
281+
"isMut": false,
282+
"isSigner": false,
283+
"isOptional": true,
284+
"docs": [
285+
"We might need it in case reallocation is needed."
286+
]
287+
}
288+
],
289+
"args": [
290+
{
291+
"name": "args",
292+
"type": {
293+
"defined": "MultisigAddVaultArgs"
294+
}
295+
}
296+
]
297+
},
244298
{
245299
"name": "configTransactionCreate",
246300
"docs": [
@@ -1288,6 +1342,32 @@
12881342
]
12891343
}
12901344
},
1345+
{
1346+
"name": "MultisigAddVaultArgs",
1347+
"type": {
1348+
"kind": "struct",
1349+
"fields": [
1350+
{
1351+
"name": "vaultIndex",
1352+
"docs": [
1353+
"The next vault index to set as the latest used.",
1354+
"Must be the current `vault_index + 1`.",
1355+
"We pass it explicitly to make this instruction idempotent."
1356+
],
1357+
"type": "u8"
1358+
},
1359+
{
1360+
"name": "memo",
1361+
"docs": [
1362+
"Memo isn't used for anything, but is included in `ChangeThreshold` that can later be parsed and indexed."
1363+
],
1364+
"type": {
1365+
"option": "string"
1366+
}
1367+
}
1368+
]
1369+
}
1370+
},
12911371
{
12921372
"name": "MultisigCreateArgs",
12931373
"type": {
@@ -1810,6 +1890,11 @@
18101890
"code": 6022,
18111891
"name": "MissingAccount",
18121892
"msg": "Missing account"
1893+
},
1894+
{
1895+
"code": 6023,
1896+
"name": "InvalidVaultIndex",
1897+
"msg": "Invalid `vault_index`"
18131898
}
18141899
],
18151900
"metadata": {

sdk/multisig/src/generated/errors/index.ts

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sdk/multisig/src/generated/instructions/index.ts

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sdk/multisig/src/generated/instructions/multisigAddVault.ts

Lines changed: 130 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)