Skip to main content

roshi_interface/instructions/
mod.rs

1pub mod args;
2
3pub use args::*;
4
5use wincode::{config::DefaultConfig, SchemaRead, SchemaWrite};
6
7pub trait InstructionArgs: SchemaWrite<DefaultConfig, Src = Self> {
8    const TAG: u8;
9}
10
11pub mod tags {
12    pub const INITIALIZE_PROGRAM: u8 = 0;
13    pub const INITIALIZE_VAULT: u8 = 1;
14    pub const AUTHORIZE_ACTION: u8 = 2;
15    pub const REVOKE_ACTION: u8 = 3;
16    pub const MANAGE: u8 = 4;
17    pub const MANAGE_BATCH: u8 = 5;
18    pub const REPORT_NAV: u8 = 6;
19    pub const DEPOSIT: u8 = 7;
20    pub const REDEEM: u8 = 8;
21    pub const CANCEL_REDEEM: u8 = 9;
22    pub const PROCESS_WITHDRAWALS: u8 = 10;
23    pub const UPDATE_VAULT_CONFIG: u8 = 11;
24    pub const INITIALIZE_ASSET: u8 = 12;
25    pub const UPDATE_ASSET: u8 = 13;
26    pub const SET_PAUSE_FLAGS: u8 = 14;
27    pub const SET_VAULT_ACCESS: u8 = 15;
28    pub const TRANSFER_PROGRAM_AUTHORITY: u8 = 16;
29    pub const TRANSFER_VAULT_AUTHORITY: u8 = 17;
30    pub const SET_STRATEGIST: u8 = 18;
31    pub const SET_NAV_AUTHORITY: u8 = 19;
32    pub const SET_WITHDRAWAL_AUTHORITY: u8 = 20;
33    pub const COLLECT_FEES: u8 = 21;
34    pub const INVEST_EXTERNAL: u8 = 22;
35    pub const RETURN_EXTERNAL: u8 = 23;
36    pub const SET_SWAP_AUTHORITY: u8 = 24;
37    pub const ATOMIC_REDEEM: u8 = 25;
38    pub const SWAP: u8 = 26;
39}
40
41// Codama parses the enum source directly and currently requires literal
42// discriminator values here; keep `tags::*` in sync via the IDL test below.
43#[repr(u8)]
44#[derive(codama_macros::CodamaInstructions)]
45#[allow(clippy::large_enum_variant)]
46#[codama(program(
47    name = "roshi",
48    address = "Roshi11111111111111111111111111111111111111"
49))]
50pub enum RoshiInstruction {
51    #[codama(account(name = "payer", signer, writable))]
52    #[codama(account(name = "program_config", writable))]
53    #[codama(account(name = "system_program", default_value = program("system")))]
54    InitializeProgram(#[codama(name = "args")] InitializeProgramArgs) = 0,
55
56    #[codama(account(name = "program_authority", signer))]
57    #[codama(account(name = "program_config"))]
58    #[codama(account(name = "payer", signer, writable))]
59    #[codama(account(name = "vault", writable))]
60    #[codama(account(name = "base_mint"))]
61    #[codama(account(name = "share_mint", writable))]
62    #[codama(account(name = "treasury"))]
63    #[codama(account(name = "system_program", default_value = program("system")))]
64    #[codama(account(name = "token_program", default_value = program("token")))]
65    InitializeVault(#[codama(name = "args")] InitializeVaultArgs) = 1,
66
67    #[codama(account(name = "admin", signer, writable))]
68    #[codama(account(name = "vault"))]
69    #[codama(account(name = "action", writable))]
70    #[codama(account(name = "system_program", default_value = program("system")))]
71    AuthorizeAction(#[codama(name = "args")] AuthorizeActionArgs) = 2,
72
73    #[codama(account(name = "admin", signer))]
74    #[codama(account(name = "vault"))]
75    #[codama(account(name = "action", writable))]
76    RevokeAction(#[codama(name = "args")] RevokeActionArgs) = 3,
77
78    #[codama(account(name = "executor", signer))]
79    #[codama(account(name = "vault"))]
80    #[codama(account(name = "sub_account", writable))]
81    #[codama(account(name = "action"))]
82    Manage(#[codama(name = "args")] ManageArgs) = 4,
83
84    #[codama(account(name = "executor", signer))]
85    #[codama(account(name = "vault"))]
86    ManageBatch(#[codama(name = "args")] ManageBatchArgs) = 5,
87
88    #[codama(account(name = "nav_authority", signer))]
89    #[codama(account(name = "vault", writable))]
90    #[codama(account(name = "share_mint"))]
91    #[codama(account(name = "base_mint"))]
92    #[codama(account(name = "deposit_base_custody"))]
93    #[codama(account(name = "withdraw_base_custody"))]
94    ReportNav(#[codama(name = "args")] ReportNavArgs) = 6,
95
96    #[codama(account(name = "depositor", signer))]
97    #[codama(account(name = "vault", writable))]
98    #[codama(account(name = "user_source_token_account", writable))]
99    #[codama(account(name = "vault_custody_token_account", writable))]
100    #[codama(account(name = "user_share_account", writable))]
101    #[codama(account(name = "share_mint", writable))]
102    #[codama(account(name = "share_token_program", default_value = program("token")))]
103    #[codama(account(name = "asset_token_program"))]
104    Deposit(#[codama(name = "args")] DepositArgs) = 7,
105
106    #[codama(account(name = "owner", signer, writable))]
107    #[codama(account(name = "vault", writable))]
108    #[codama(account(name = "user_share_account", writable))]
109    #[codama(account(name = "share_mint", writable))]
110    #[codama(account(name = "recipient_token_account"))]
111    #[codama(account(name = "withdrawal_ticket", writable))]
112    #[codama(account(name = "system_program", default_value = program("system")))]
113    #[codama(account(name = "token_program", default_value = program("token")))]
114    Redeem(#[codama(name = "args")] RedeemArgs) = 8,
115
116    #[codama(account(name = "owner", signer, writable))]
117    #[codama(account(name = "vault", writable))]
118    #[codama(account(name = "withdrawal_ticket", writable))]
119    #[codama(account(name = "share_mint", writable))]
120    #[codama(account(name = "owner_share_account", writable))]
121    #[codama(account(name = "token_program", default_value = program("token")))]
122    CancelRedeem(#[codama(name = "args")] CancelRedeemArgs) = 9,
123
124    #[codama(account(name = "withdrawal_authority", signer))]
125    #[codama(account(name = "vault", writable))]
126    #[codama(account(name = "withdraw_sub_account"))]
127    #[codama(account(name = "custody", writable))]
128    #[codama(account(name = "share_mint"))]
129    #[codama(account(name = "token_program", default_value = program("token")))]
130    ProcessWithdrawals = 10,
131
132    #[codama(account(name = "admin", signer))]
133    #[codama(account(name = "vault", writable))]
134    #[codama(account(name = "treasury"))]
135    UpdateVaultConfig(#[codama(name = "args")] UpdateVaultConfigArgs) = 11,
136
137    #[codama(account(name = "admin", signer, writable))]
138    #[codama(account(name = "vault"))]
139    #[codama(account(name = "asset_mint"))]
140    #[codama(account(name = "asset", writable))]
141    #[codama(account(name = "system_program", default_value = program("system")))]
142    InitializeAsset(#[codama(name = "args")] InitializeAssetArgs) = 12,
143
144    #[codama(account(name = "admin", signer))]
145    #[codama(account(name = "vault"))]
146    #[codama(account(name = "asset", writable))]
147    UpdateAsset(#[codama(name = "args")] UpdateAssetArgs) = 13,
148
149    #[codama(account(name = "admin", signer))]
150    #[codama(account(name = "vault", writable))]
151    SetPauseFlags(#[codama(name = "args")] SetPauseFlagsArgs) = 14,
152
153    #[codama(account(name = "admin", signer))]
154    #[codama(account(name = "vault", writable))]
155    SetVaultAccess(#[codama(name = "args")] SetVaultAccessArgs) = 15,
156
157    #[codama(account(name = "authority", signer))]
158    #[codama(account(name = "program_config", writable))]
159    TransferProgramAuthority(#[codama(name = "args")] TransferProgramAuthorityArgs) = 16,
160
161    #[codama(account(name = "admin", signer))]
162    #[codama(account(name = "vault", writable))]
163    TransferVaultAuthority(#[codama(name = "args")] TransferVaultAuthorityArgs) = 17,
164
165    #[codama(account(name = "admin", signer))]
166    #[codama(account(name = "vault", writable))]
167    SetStrategist(#[codama(name = "args")] SetStrategistArgs) = 18,
168
169    #[codama(account(name = "admin", signer))]
170    #[codama(account(name = "vault", writable))]
171    SetNavAuthority(#[codama(name = "args")] SetNavAuthorityArgs) = 19,
172
173    #[codama(account(name = "admin", signer))]
174    #[codama(account(name = "vault", writable))]
175    SetWithdrawalAuthority(#[codama(name = "args")] SetWithdrawalAuthorityArgs) = 20,
176
177    #[codama(account(name = "admin", signer))]
178    #[codama(account(name = "vault", writable))]
179    #[codama(account(name = "fee_sub_account"))]
180    #[codama(account(name = "custody", writable))]
181    #[codama(account(name = "treasury", writable))]
182    #[codama(account(name = "token_program", default_value = program("token")))]
183    CollectFees(#[codama(name = "args")] CollectFeesArgs) = 21,
184
185    #[codama(account(name = "strategist", signer))]
186    #[codama(account(name = "vault", writable))]
187    #[codama(account(name = "sub_account"))]
188    #[codama(account(name = "custody", writable))]
189    #[codama(account(name = "external_account", writable))]
190    #[codama(account(name = "token_program", default_value = program("token")))]
191    InvestExternal(#[codama(name = "args")] InvestExternalArgs) = 22,
192
193    #[codama(account(name = "strategist", signer))]
194    #[codama(account(name = "external_authority", signer))]
195    #[codama(account(name = "vault", writable))]
196    #[codama(account(name = "sub_account"))]
197    #[codama(account(name = "external_account", writable))]
198    #[codama(account(name = "custody", writable))]
199    #[codama(account(name = "token_program", default_value = program("token")))]
200    ReturnExternal(#[codama(name = "args")] ReturnExternalArgs) = 23,
201
202    #[codama(account(name = "admin", signer))]
203    #[codama(account(name = "vault", writable))]
204    SetSwapAuthority(#[codama(name = "args")] SetSwapAuthorityArgs) = 24,
205
206    #[codama(account(name = "owner", signer, writable))]
207    #[codama(account(name = "vault", writable))]
208    #[codama(account(name = "user_share_account", writable))]
209    #[codama(account(name = "share_mint", writable))]
210    #[codama(account(name = "recipient_token_account", writable))]
211    #[codama(account(name = "custody", writable))]
212    #[codama(account(name = "base_token_program"))]
213    #[codama(account(name = "sub_account"))]
214    #[codama(account(name = "action"))]
215    #[codama(account(name = "token_program", default_value = program("token")))]
216    AtomicRedeem(#[codama(name = "args")] AtomicRedeemArgs) = 25,
217
218    #[codama(account(name = "swap_authority", signer))]
219    #[codama(account(name = "vault"))]
220    #[codama(account(name = "sub_account"))]
221    #[codama(account(name = "input_custody", writable))]
222    #[codama(account(name = "output_custody", writable))]
223    #[codama(account(name = "action"))]
224    Swap(#[codama(name = "args")] SwapArgs) = 26,
225}
226
227impl RoshiInstruction {
228    pub const fn tag(&self) -> u8 {
229        match self {
230            Self::InitializeProgram(_) => tags::INITIALIZE_PROGRAM,
231            Self::InitializeVault(_) => tags::INITIALIZE_VAULT,
232            Self::AuthorizeAction(_) => tags::AUTHORIZE_ACTION,
233            Self::RevokeAction(_) => tags::REVOKE_ACTION,
234            Self::Manage(_) => tags::MANAGE,
235            Self::ManageBatch(_) => tags::MANAGE_BATCH,
236            Self::ReportNav(_) => tags::REPORT_NAV,
237            Self::Deposit(_) => tags::DEPOSIT,
238            Self::Redeem(_) => tags::REDEEM,
239            Self::CancelRedeem(_) => tags::CANCEL_REDEEM,
240            Self::ProcessWithdrawals => tags::PROCESS_WITHDRAWALS,
241            Self::UpdateVaultConfig(_) => tags::UPDATE_VAULT_CONFIG,
242            Self::InitializeAsset(_) => tags::INITIALIZE_ASSET,
243            Self::UpdateAsset(_) => tags::UPDATE_ASSET,
244            Self::InvestExternal(_) => tags::INVEST_EXTERNAL,
245            Self::ReturnExternal(_) => tags::RETURN_EXTERNAL,
246            Self::SetPauseFlags(_) => tags::SET_PAUSE_FLAGS,
247            Self::SetVaultAccess(_) => tags::SET_VAULT_ACCESS,
248            Self::TransferProgramAuthority(_) => tags::TRANSFER_PROGRAM_AUTHORITY,
249            Self::TransferVaultAuthority(_) => tags::TRANSFER_VAULT_AUTHORITY,
250            Self::SetStrategist(_) => tags::SET_STRATEGIST,
251            Self::SetSwapAuthority(_) => tags::SET_SWAP_AUTHORITY,
252            Self::SetNavAuthority(_) => tags::SET_NAV_AUTHORITY,
253            Self::SetWithdrawalAuthority(_) => tags::SET_WITHDRAWAL_AUTHORITY,
254            Self::CollectFees(_) => tags::COLLECT_FEES,
255            Self::AtomicRedeem(_) => tags::ATOMIC_REDEEM,
256            Self::Swap(_) => tags::SWAP,
257        }
258    }
259
260    #[allow(clippy::result_unit_err)]
261    pub fn decode(data: &[u8]) -> Result<Self, ()> {
262        let (tag, payload) = data.split_first().ok_or(())?;
263
264        match *tag {
265            tags::INITIALIZE_PROGRAM => Ok(Self::InitializeProgram(decode_payload(payload)?)),
266            tags::INITIALIZE_VAULT => Ok(Self::InitializeVault(decode_payload(payload)?)),
267            tags::AUTHORIZE_ACTION => Ok(Self::AuthorizeAction(decode_payload(payload)?)),
268            tags::REVOKE_ACTION => Ok(Self::RevokeAction(decode_payload(payload)?)),
269            tags::MANAGE => Ok(Self::Manage(decode_payload(payload)?)),
270            tags::MANAGE_BATCH => Ok(Self::ManageBatch(decode_payload(payload)?)),
271            tags::REPORT_NAV => Ok(Self::ReportNav(decode_payload(payload)?)),
272            tags::DEPOSIT => Ok(Self::Deposit(decode_payload(payload)?)),
273            tags::REDEEM => Ok(Self::Redeem(decode_payload(payload)?)),
274            tags::CANCEL_REDEEM => Ok(Self::CancelRedeem(decode_payload(payload)?)),
275            tags::PROCESS_WITHDRAWALS => {
276                let ProcessWithdrawalsArgs = decode_payload(payload)?;
277                Ok(Self::ProcessWithdrawals)
278            }
279            tags::UPDATE_VAULT_CONFIG => Ok(Self::UpdateVaultConfig(decode_payload(payload)?)),
280            tags::INITIALIZE_ASSET => Ok(Self::InitializeAsset(decode_payload(payload)?)),
281            tags::UPDATE_ASSET => Ok(Self::UpdateAsset(decode_payload(payload)?)),
282            tags::INVEST_EXTERNAL => Ok(Self::InvestExternal(decode_payload(payload)?)),
283            tags::RETURN_EXTERNAL => Ok(Self::ReturnExternal(decode_payload(payload)?)),
284            tags::SET_PAUSE_FLAGS => Ok(Self::SetPauseFlags(decode_payload(payload)?)),
285            tags::SET_VAULT_ACCESS => Ok(Self::SetVaultAccess(decode_payload(payload)?)),
286            tags::TRANSFER_PROGRAM_AUTHORITY => {
287                Ok(Self::TransferProgramAuthority(decode_payload(payload)?))
288            }
289            tags::TRANSFER_VAULT_AUTHORITY => {
290                Ok(Self::TransferVaultAuthority(decode_payload(payload)?))
291            }
292            tags::SET_STRATEGIST => Ok(Self::SetStrategist(decode_payload(payload)?)),
293            tags::SET_SWAP_AUTHORITY => Ok(Self::SetSwapAuthority(decode_payload(payload)?)),
294            tags::SET_NAV_AUTHORITY => Ok(Self::SetNavAuthority(decode_payload(payload)?)),
295            tags::SET_WITHDRAWAL_AUTHORITY => {
296                Ok(Self::SetWithdrawalAuthority(decode_payload(payload)?))
297            }
298            tags::COLLECT_FEES => Ok(Self::CollectFees(decode_payload(payload)?)),
299            tags::ATOMIC_REDEEM => Ok(Self::AtomicRedeem(decode_payload(payload)?)),
300            tags::SWAP => Ok(Self::Swap(decode_payload(payload)?)),
301            _ => Err(()),
302        }
303    }
304
305    pub fn serialize(&self) -> Result<Vec<u8>, wincode::WriteError> {
306        let mut data = vec![self.tag()];
307
308        match self {
309            Self::InitializeProgram(args) => wincode::serialize_into(&mut data, args)?,
310            Self::InitializeVault(args) => wincode::serialize_into(&mut data, args)?,
311            Self::AuthorizeAction(args) => wincode::serialize_into(&mut data, args)?,
312            Self::RevokeAction(args) => wincode::serialize_into(&mut data, args)?,
313            Self::Manage(args) => wincode::serialize_into(&mut data, args)?,
314            Self::ManageBatch(args) => wincode::serialize_into(&mut data, args)?,
315            Self::ReportNav(args) => wincode::serialize_into(&mut data, args)?,
316            Self::Deposit(args) => wincode::serialize_into(&mut data, args)?,
317            Self::Redeem(args) => wincode::serialize_into(&mut data, args)?,
318            Self::CancelRedeem(args) => wincode::serialize_into(&mut data, args)?,
319            Self::ProcessWithdrawals => {
320                wincode::serialize_into(&mut data, &ProcessWithdrawalsArgs)?
321            }
322            Self::UpdateVaultConfig(args) => wincode::serialize_into(&mut data, args)?,
323            Self::InitializeAsset(args) => wincode::serialize_into(&mut data, args)?,
324            Self::UpdateAsset(args) => wincode::serialize_into(&mut data, args)?,
325            Self::InvestExternal(args) => wincode::serialize_into(&mut data, args)?,
326            Self::ReturnExternal(args) => wincode::serialize_into(&mut data, args)?,
327            Self::SetPauseFlags(args) => wincode::serialize_into(&mut data, args)?,
328            Self::SetVaultAccess(args) => wincode::serialize_into(&mut data, args)?,
329            Self::TransferProgramAuthority(args) => wincode::serialize_into(&mut data, args)?,
330            Self::TransferVaultAuthority(args) => wincode::serialize_into(&mut data, args)?,
331            Self::SetStrategist(args) => wincode::serialize_into(&mut data, args)?,
332            Self::SetSwapAuthority(args) => wincode::serialize_into(&mut data, args)?,
333            Self::SetNavAuthority(args) => wincode::serialize_into(&mut data, args)?,
334            Self::SetWithdrawalAuthority(args) => wincode::serialize_into(&mut data, args)?,
335            Self::CollectFees(args) => wincode::serialize_into(&mut data, args)?,
336            Self::AtomicRedeem(args) => wincode::serialize_into(&mut data, args)?,
337            Self::Swap(args) => wincode::serialize_into(&mut data, args)?,
338        }
339
340        Ok(data)
341    }
342}
343
344fn decode_payload<'a, T>(payload: &'a [u8]) -> Result<T, ()>
345where
346    T: SchemaRead<'a, DefaultConfig, Dst = T>,
347{
348    wincode::deserialize_exact(payload).map_err(|_| ())
349}
350
351macro_rules! impl_instruction_args {
352    ($( $args:ty = $tag:expr ),+ $(,)?) => {
353        $(
354            impl InstructionArgs for $args {
355                const TAG: u8 = $tag;
356            }
357        )+
358
359        #[cfg(test)]
360        const TAG_CASES: &[u8] = &[
361            $(
362                $tag,
363            )+
364        ];
365    };
366}
367
368impl_instruction_args! {
369    InitializeProgramArgs = tags::INITIALIZE_PROGRAM,
370    InitializeVaultArgs = tags::INITIALIZE_VAULT,
371    AuthorizeActionArgs = tags::AUTHORIZE_ACTION,
372    RevokeActionArgs = tags::REVOKE_ACTION,
373    ManageArgs = tags::MANAGE,
374    ManageBatchArgs = tags::MANAGE_BATCH,
375    ReportNavArgs = tags::REPORT_NAV,
376    DepositArgs = tags::DEPOSIT,
377    RedeemArgs = tags::REDEEM,
378    CancelRedeemArgs = tags::CANCEL_REDEEM,
379    ProcessWithdrawalsArgs = tags::PROCESS_WITHDRAWALS,
380    UpdateVaultConfigArgs = tags::UPDATE_VAULT_CONFIG,
381    InitializeAssetArgs = tags::INITIALIZE_ASSET,
382    UpdateAssetArgs = tags::UPDATE_ASSET,
383    SetPauseFlagsArgs = tags::SET_PAUSE_FLAGS,
384    SetVaultAccessArgs = tags::SET_VAULT_ACCESS,
385    TransferProgramAuthorityArgs = tags::TRANSFER_PROGRAM_AUTHORITY,
386    TransferVaultAuthorityArgs = tags::TRANSFER_VAULT_AUTHORITY,
387    SetStrategistArgs = tags::SET_STRATEGIST,
388    SetNavAuthorityArgs = tags::SET_NAV_AUTHORITY,
389    SetWithdrawalAuthorityArgs = tags::SET_WITHDRAWAL_AUTHORITY,
390    CollectFeesArgs = tags::COLLECT_FEES,
391    InvestExternalArgs = tags::INVEST_EXTERNAL,
392    ReturnExternalArgs = tags::RETURN_EXTERNAL,
393    SetSwapAuthorityArgs = tags::SET_SWAP_AUTHORITY,
394    AtomicRedeemArgs = tags::ATOMIC_REDEEM,
395    SwapArgs = tags::SWAP,
396}
397
398pub fn serialize_instruction<T>(args: &T) -> Result<Vec<u8>, wincode::WriteError>
399where
400    T: InstructionArgs,
401{
402    let mut data = vec![T::TAG];
403    wincode::serialize_into(&mut data, args)?;
404    Ok(data)
405}
406
407#[cfg(test)]
408mod tests {
409    use super::*;
410    use codama::{Codama, NodeTrait};
411    use serde_json::Value;
412    use std::path::Path;
413    use wincode::deserialize_exact;
414
415    #[test]
416    fn instruction_args_tags_match_canonical_tags() {
417        assert_eq!(
418            TAG_CASES,
419            &[
420                0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
421                23, 24, 25, 26
422            ]
423        );
424        assert_eq!(
425            RoshiInstruction::ProcessWithdrawals.tag(),
426            <ProcessWithdrawalsArgs as InstructionArgs>::TAG
427        );
428    }
429
430    #[test]
431    fn codama_idl_uses_canonical_instruction_discriminators() {
432        let mut idl = Codama::load(Path::new(env!("CARGO_MANIFEST_DIR")))
433            .unwrap()
434            .get_idl()
435            .unwrap();
436        idl.program.name = "roshi".into();
437        let idl: Value = serde_json::from_str(&idl.to_json().unwrap()).unwrap();
438        let instructions = idl["program"]["instructions"].as_array().unwrap();
439
440        assert_eq!(idl["program"]["name"], "roshi");
441        assert_eq!(
442            idl["program"]["publicKey"],
443            "Roshi11111111111111111111111111111111111111"
444        );
445        assert_eq!(instructions.len(), TAG_CASES.len());
446
447        for (name, tag) in IDL_TAG_CASES {
448            assert_instruction_discriminator(instructions, name, *tag);
449        }
450
451        let deposit = instruction(instructions, "deposit");
452        assert_eq!(deposit["arguments"][1]["name"], "args");
453        assert_eq!(deposit["arguments"][1]["type"]["name"], "depositArgs");
454
455        let process_withdrawals = instruction(instructions, "processWithdrawals");
456        assert_eq!(
457            process_withdrawals["arguments"].as_array().unwrap().len(),
458            1
459        );
460    }
461
462    #[test]
463    fn instruction_decode_rejects_unknown_values() {
464        assert!(RoshiInstruction::decode(&[255]).is_err());
465    }
466
467    #[test]
468    fn serialize_instruction_writes_tag_then_args_payload() {
469        let args = DepositArgs {
470            asset_mint: [4; 32],
471            amount: 123,
472            min_shares_out: 456,
473            access_proof: vec![[1; 32], [2; 32], [3; 32]],
474        };
475
476        let encoded = serialize_instruction(&args).unwrap();
477        let decoded: DepositArgs = deserialize_exact(&encoded[1..]).unwrap();
478
479        assert_eq!(encoded[0], <DepositArgs as InstructionArgs>::TAG);
480        assert_eq!(decoded.asset_mint, [4; 32]);
481        assert_eq!(decoded.amount, 123);
482        assert_eq!(decoded.min_shares_out, 456);
483        assert_eq!(decoded.access_proof, vec![[1; 32], [2; 32], [3; 32]]);
484    }
485
486    #[test]
487    fn canonical_instruction_serializes_like_args_helper() {
488        let args = DepositArgs {
489            asset_mint: [4; 32],
490            amount: 123,
491            min_shares_out: 456,
492            access_proof: vec![[1; 32]],
493        };
494
495        assert_eq!(
496            RoshiInstruction::Deposit(args).serialize().unwrap(),
497            serialize_instruction(&DepositArgs {
498                asset_mint: [4; 32],
499                amount: 123,
500                min_shares_out: 456,
501                access_proof: vec![[1; 32]],
502            })
503            .unwrap()
504        );
505    }
506
507    #[test]
508    fn serialize_zero_sized_args_writes_only_tag() {
509        assert_eq!(
510            serialize_instruction(&ProcessWithdrawalsArgs).unwrap(),
511            vec![<ProcessWithdrawalsArgs as InstructionArgs>::TAG]
512        );
513        assert_eq!(
514            RoshiInstruction::ProcessWithdrawals.serialize().unwrap(),
515            vec![<ProcessWithdrawalsArgs as InstructionArgs>::TAG]
516        );
517    }
518
519    fn instruction<'a>(instructions: &'a [Value], name: &str) -> &'a Value {
520        instructions
521            .iter()
522            .find(|instruction| instruction["name"] == name)
523            .unwrap()
524    }
525
526    fn assert_instruction_discriminator(instructions: &[Value], name: &str, tag: u8) {
527        assert_eq!(
528            instruction(instructions, name)["arguments"][0]["defaultValue"]["number"],
529            u64::from(tag)
530        );
531    }
532
533    const IDL_TAG_CASES: &[(&str, u8)] = &[
534        ("initializeProgram", tags::INITIALIZE_PROGRAM),
535        ("initializeVault", tags::INITIALIZE_VAULT),
536        ("authorizeAction", tags::AUTHORIZE_ACTION),
537        ("revokeAction", tags::REVOKE_ACTION),
538        ("manage", tags::MANAGE),
539        ("manageBatch", tags::MANAGE_BATCH),
540        ("reportNav", tags::REPORT_NAV),
541        ("deposit", tags::DEPOSIT),
542        ("redeem", tags::REDEEM),
543        ("cancelRedeem", tags::CANCEL_REDEEM),
544        ("processWithdrawals", tags::PROCESS_WITHDRAWALS),
545        ("updateVaultConfig", tags::UPDATE_VAULT_CONFIG),
546        ("initializeAsset", tags::INITIALIZE_ASSET),
547        ("updateAsset", tags::UPDATE_ASSET),
548        ("setPauseFlags", tags::SET_PAUSE_FLAGS),
549        ("setVaultAccess", tags::SET_VAULT_ACCESS),
550        ("transferProgramAuthority", tags::TRANSFER_PROGRAM_AUTHORITY),
551        ("transferVaultAuthority", tags::TRANSFER_VAULT_AUTHORITY),
552        ("setStrategist", tags::SET_STRATEGIST),
553        ("setNavAuthority", tags::SET_NAV_AUTHORITY),
554        ("setWithdrawalAuthority", tags::SET_WITHDRAWAL_AUTHORITY),
555        ("collectFees", tags::COLLECT_FEES),
556        ("investExternal", tags::INVEST_EXTERNAL),
557        ("returnExternal", tags::RETURN_EXTERNAL),
558        ("setSwapAuthority", tags::SET_SWAP_AUTHORITY),
559        ("atomicRedeem", tags::ATOMIC_REDEEM),
560        ("swap", tags::SWAP),
561    ];
562}