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