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