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