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    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    #[codama(account(name = "admin", signer))]
211    #[codama(account(name = "vault", writable))]
212    SetSwapAuthority(#[codama(name = "args")] SetSwapAuthorityArgs) = 24,
213
214    #[codama(account(name = "owner", signer, writable))]
215    #[codama(account(name = "vault", writable))]
216    #[codama(account(name = "user_share_account", writable))]
217    #[codama(account(name = "share_mint", writable))]
218    #[codama(account(name = "recipient_token_account", writable))]
219    #[codama(account(name = "custody", writable))]
220    #[codama(account(name = "base_token_program"))]
221    #[codama(account(name = "sub_account"))]
222    #[codama(account(name = "action"))]
223    #[codama(account(name = "token_program", default_value = program("token")))]
224    AtomicRedeem(#[codama(name = "args")] AtomicRedeemArgs) = 25,
225
226    #[codama(account(name = "swap_authority", signer))]
227    #[codama(account(name = "vault"))]
228    #[codama(account(name = "sub_account"))]
229    #[codama(account(name = "input_custody", writable))]
230    #[codama(account(name = "output_custody", writable))]
231    #[codama(account(name = "action"))]
232    Swap(#[codama(name = "args")] SwapArgs) = 26,
233
234    #[codama(account(name = "admin", signer))]
235    #[codama(account(name = "vault", writable))]
236    WriteDownFees(#[codama(name = "args")] WriteDownFeesArgs) = 27,
237
238    #[codama(account(name = "admin", signer, writable))]
239    #[codama(account(name = "vault"))]
240    #[codama(account(name = "destination_token_account"))]
241    #[codama(account(name = "external_destination", writable))]
242    #[codama(account(name = "system_program", default_value = program("system")))]
243    RegisterExternalDestination = 28,
244
245    #[codama(account(name = "admin", signer, writable))]
246    #[codama(account(name = "vault"))]
247    #[codama(account(name = "external_destination", writable))]
248    RevokeExternalDestination = 29,
249
250    #[codama(account(name = "admin", signer, writable))]
251    #[codama(account(name = "vault"))]
252    #[codama(account(name = "share_mint"))]
253    #[codama(account(name = "metadata", writable))]
254    #[codama(account(name = "token_metadata_program"))]
255    #[codama(account(name = "system_program", default_value = program("system")))]
256    SetShareMetadata(#[codama(name = "args")] SetShareMetadataArgs) = 30,
257
258    #[codama(account(name = "token_account"))]
259    AssertDelegateCleared = 31,
260
261    #[codama(account(name = "admin", signer))]
262    #[codama(account(name = "vault"))]
263    #[codama(account(name = "action", writable))]
264    AdminSetFlashFeeRate(#[codama(name = "args")] AdminSetFlashFeeRateArgs) = 32,
265
266    #[codama(account(name = "strategist", signer))]
267    #[codama(account(name = "vault"))]
268    #[codama(account(name = "action", writable))]
269    StrategistLowerFlashFeeRate(#[codama(name = "args")] StrategistLowerFlashFeeRateArgs) = 33,
270}
271
272impl RoshiInstruction {
273    pub const fn tag(&self) -> u8 {
274        match self {
275            Self::InitializeProgram(_) => tags::INITIALIZE_PROGRAM,
276            Self::InitializeVault(_) => tags::INITIALIZE_VAULT,
277            Self::AuthorizeAction(_) => tags::AUTHORIZE_ACTION,
278            Self::RevokeAction(_) => tags::REVOKE_ACTION,
279            Self::Manage(_) => tags::MANAGE,
280            Self::ManageBatch(_) => tags::MANAGE_BATCH,
281            Self::ReportNav(_) => tags::REPORT_NAV,
282            Self::Deposit(_) => tags::DEPOSIT,
283            Self::Redeem(_) => tags::REDEEM,
284            Self::CancelRedeem(_) => tags::CANCEL_REDEEM,
285            Self::ProcessWithdrawals => tags::PROCESS_WITHDRAWALS,
286            Self::UpdateVaultConfig(_) => tags::UPDATE_VAULT_CONFIG,
287            Self::InitializeAsset(_) => tags::INITIALIZE_ASSET,
288            Self::UpdateAsset(_) => tags::UPDATE_ASSET,
289            Self::InvestExternal(_) => tags::INVEST_EXTERNAL,
290            Self::ReturnExternal(_) => tags::RETURN_EXTERNAL,
291            Self::SetPauseFlags(_) => tags::SET_PAUSE_FLAGS,
292            Self::SetVaultAccess(_) => tags::SET_VAULT_ACCESS,
293            Self::TransferProgramAuthority(_) => tags::TRANSFER_PROGRAM_AUTHORITY,
294            Self::TransferVaultAuthority(_) => tags::TRANSFER_VAULT_AUTHORITY,
295            Self::SetStrategist(_) => tags::SET_STRATEGIST,
296            Self::SetSwapAuthority(_) => tags::SET_SWAP_AUTHORITY,
297            Self::SetNavAuthority(_) => tags::SET_NAV_AUTHORITY,
298            Self::SetWithdrawalAuthority(_) => tags::SET_WITHDRAWAL_AUTHORITY,
299            Self::CollectFees(_) => tags::COLLECT_FEES,
300            Self::AtomicRedeem(_) => tags::ATOMIC_REDEEM,
301            Self::Swap(_) => tags::SWAP,
302            Self::WriteDownFees(_) => tags::WRITE_DOWN_FEES,
303            Self::RegisterExternalDestination => tags::REGISTER_EXTERNAL_DESTINATION,
304            Self::RevokeExternalDestination => tags::REVOKE_EXTERNAL_DESTINATION,
305            Self::SetShareMetadata(_) => tags::SET_SHARE_METADATA,
306            Self::AssertDelegateCleared => tags::ASSERT_DELEGATE_CLEARED,
307            Self::AdminSetFlashFeeRate(_) => tags::ADMIN_SET_FLASH_FEE_RATE,
308            Self::StrategistLowerFlashFeeRate(_) => tags::STRATEGIST_LOWER_FLASH_FEE_RATE,
309        }
310    }
311
312    #[allow(clippy::result_unit_err)]
313    pub fn decode(data: &[u8]) -> Result<Self, ()> {
314        let (tag, payload) = data.split_first().ok_or(())?;
315
316        match *tag {
317            tags::INITIALIZE_PROGRAM => Ok(Self::InitializeProgram(decode_payload(payload)?)),
318            tags::INITIALIZE_VAULT => Ok(Self::InitializeVault(decode_payload(payload)?)),
319            tags::AUTHORIZE_ACTION => Ok(Self::AuthorizeAction(decode_payload(payload)?)),
320            tags::REVOKE_ACTION => Ok(Self::RevokeAction(decode_payload(payload)?)),
321            tags::MANAGE => Ok(Self::Manage(decode_payload(payload)?)),
322            tags::MANAGE_BATCH => Ok(Self::ManageBatch(decode_payload(payload)?)),
323            tags::REPORT_NAV => Ok(Self::ReportNav(decode_payload(payload)?)),
324            tags::DEPOSIT => Ok(Self::Deposit(decode_payload(payload)?)),
325            tags::REDEEM => Ok(Self::Redeem(decode_payload(payload)?)),
326            tags::CANCEL_REDEEM => Ok(Self::CancelRedeem(decode_payload(payload)?)),
327            tags::PROCESS_WITHDRAWALS => {
328                let ProcessWithdrawalsArgs = decode_payload(payload)?;
329                Ok(Self::ProcessWithdrawals)
330            }
331            tags::UPDATE_VAULT_CONFIG => Ok(Self::UpdateVaultConfig(decode_payload(payload)?)),
332            tags::INITIALIZE_ASSET => Ok(Self::InitializeAsset(decode_payload(payload)?)),
333            tags::UPDATE_ASSET => Ok(Self::UpdateAsset(decode_payload(payload)?)),
334            tags::INVEST_EXTERNAL => Ok(Self::InvestExternal(decode_payload(payload)?)),
335            tags::RETURN_EXTERNAL => Ok(Self::ReturnExternal(decode_payload(payload)?)),
336            tags::SET_PAUSE_FLAGS => Ok(Self::SetPauseFlags(decode_payload(payload)?)),
337            tags::SET_VAULT_ACCESS => Ok(Self::SetVaultAccess(decode_payload(payload)?)),
338            tags::TRANSFER_PROGRAM_AUTHORITY => {
339                Ok(Self::TransferProgramAuthority(decode_payload(payload)?))
340            }
341            tags::TRANSFER_VAULT_AUTHORITY => {
342                Ok(Self::TransferVaultAuthority(decode_payload(payload)?))
343            }
344            tags::SET_STRATEGIST => Ok(Self::SetStrategist(decode_payload(payload)?)),
345            tags::SET_SWAP_AUTHORITY => Ok(Self::SetSwapAuthority(decode_payload(payload)?)),
346            tags::SET_NAV_AUTHORITY => Ok(Self::SetNavAuthority(decode_payload(payload)?)),
347            tags::SET_WITHDRAWAL_AUTHORITY => {
348                Ok(Self::SetWithdrawalAuthority(decode_payload(payload)?))
349            }
350            tags::COLLECT_FEES => Ok(Self::CollectFees(decode_payload(payload)?)),
351            tags::ATOMIC_REDEEM => Ok(Self::AtomicRedeem(decode_payload(payload)?)),
352            tags::SWAP => Ok(Self::Swap(decode_payload(payload)?)),
353            tags::WRITE_DOWN_FEES => Ok(Self::WriteDownFees(decode_payload(payload)?)),
354            tags::REGISTER_EXTERNAL_DESTINATION => {
355                let RegisterExternalDestinationArgs = decode_payload(payload)?;
356                Ok(Self::RegisterExternalDestination)
357            }
358            tags::REVOKE_EXTERNAL_DESTINATION => {
359                let RevokeExternalDestinationArgs = decode_payload(payload)?;
360                Ok(Self::RevokeExternalDestination)
361            }
362            tags::SET_SHARE_METADATA => Ok(Self::SetShareMetadata(decode_payload(payload)?)),
363            tags::ASSERT_DELEGATE_CLEARED => {
364                let AssertDelegateClearedArgs = decode_payload(payload)?;
365                Ok(Self::AssertDelegateCleared)
366            }
367            tags::ADMIN_SET_FLASH_FEE_RATE => {
368                Ok(Self::AdminSetFlashFeeRate(decode_payload(payload)?))
369            }
370            tags::STRATEGIST_LOWER_FLASH_FEE_RATE => {
371                Ok(Self::StrategistLowerFlashFeeRate(decode_payload(payload)?))
372            }
373            _ => Err(()),
374        }
375    }
376
377    pub fn serialize(&self) -> Result<Vec<u8>, wincode::WriteError> {
378        let mut data = vec![self.tag()];
379
380        match self {
381            Self::InitializeProgram(args) => wincode::serialize_into(&mut data, args)?,
382            Self::InitializeVault(args) => wincode::serialize_into(&mut data, args)?,
383            Self::AuthorizeAction(args) => wincode::serialize_into(&mut data, args)?,
384            Self::RevokeAction(args) => wincode::serialize_into(&mut data, args)?,
385            Self::Manage(args) => wincode::serialize_into(&mut data, args)?,
386            Self::ManageBatch(args) => wincode::serialize_into(&mut data, args)?,
387            Self::ReportNav(args) => wincode::serialize_into(&mut data, args)?,
388            Self::Deposit(args) => wincode::serialize_into(&mut data, args)?,
389            Self::Redeem(args) => wincode::serialize_into(&mut data, args)?,
390            Self::CancelRedeem(args) => wincode::serialize_into(&mut data, args)?,
391            Self::ProcessWithdrawals => {
392                wincode::serialize_into(&mut data, &ProcessWithdrawalsArgs)?
393            }
394            Self::UpdateVaultConfig(args) => wincode::serialize_into(&mut data, args)?,
395            Self::InitializeAsset(args) => wincode::serialize_into(&mut data, args)?,
396            Self::UpdateAsset(args) => wincode::serialize_into(&mut data, args)?,
397            Self::InvestExternal(args) => wincode::serialize_into(&mut data, args)?,
398            Self::ReturnExternal(args) => wincode::serialize_into(&mut data, args)?,
399            Self::SetPauseFlags(args) => wincode::serialize_into(&mut data, args)?,
400            Self::SetVaultAccess(args) => wincode::serialize_into(&mut data, args)?,
401            Self::TransferProgramAuthority(args) => wincode::serialize_into(&mut data, args)?,
402            Self::TransferVaultAuthority(args) => wincode::serialize_into(&mut data, args)?,
403            Self::SetStrategist(args) => wincode::serialize_into(&mut data, args)?,
404            Self::SetSwapAuthority(args) => wincode::serialize_into(&mut data, args)?,
405            Self::SetNavAuthority(args) => wincode::serialize_into(&mut data, args)?,
406            Self::SetWithdrawalAuthority(args) => wincode::serialize_into(&mut data, args)?,
407            Self::CollectFees(args) => wincode::serialize_into(&mut data, args)?,
408            Self::AtomicRedeem(args) => wincode::serialize_into(&mut data, args)?,
409            Self::Swap(args) => wincode::serialize_into(&mut data, args)?,
410            Self::WriteDownFees(args) => wincode::serialize_into(&mut data, args)?,
411            Self::RegisterExternalDestination => {
412                wincode::serialize_into(&mut data, &RegisterExternalDestinationArgs)?
413            }
414            Self::RevokeExternalDestination => {
415                wincode::serialize_into(&mut data, &RevokeExternalDestinationArgs)?
416            }
417            Self::SetShareMetadata(args) => wincode::serialize_into(&mut data, args)?,
418            Self::AssertDelegateCleared => {
419                wincode::serialize_into(&mut data, &AssertDelegateClearedArgs)?
420            }
421            Self::AdminSetFlashFeeRate(args) => wincode::serialize_into(&mut data, args)?,
422            Self::StrategistLowerFlashFeeRate(args) => wincode::serialize_into(&mut data, args)?,
423        }
424
425        Ok(data)
426    }
427}
428
429fn decode_payload<'a, T>(payload: &'a [u8]) -> Result<T, ()>
430where
431    T: SchemaRead<'a, DefaultConfig, Dst = T>,
432{
433    wincode::deserialize_exact(payload).map_err(|_| ())
434}
435
436macro_rules! impl_instruction_args {
437    ($( $args:ty = $tag:expr ),+ $(,)?) => {
438        $(
439            impl InstructionArgs for $args {
440                const TAG: u8 = $tag;
441            }
442        )+
443
444        #[cfg(test)]
445        const TAG_CASES: &[u8] = &[
446            $(
447                $tag,
448            )+
449        ];
450    };
451}
452
453impl_instruction_args! {
454    InitializeProgramArgs = tags::INITIALIZE_PROGRAM,
455    InitializeVaultArgs = tags::INITIALIZE_VAULT,
456    AuthorizeActionArgs = tags::AUTHORIZE_ACTION,
457    RevokeActionArgs = tags::REVOKE_ACTION,
458    ManageArgs = tags::MANAGE,
459    ManageBatchArgs = tags::MANAGE_BATCH,
460    ReportNavArgs = tags::REPORT_NAV,
461    DepositArgs = tags::DEPOSIT,
462    RedeemArgs = tags::REDEEM,
463    CancelRedeemArgs = tags::CANCEL_REDEEM,
464    ProcessWithdrawalsArgs = tags::PROCESS_WITHDRAWALS,
465    UpdateVaultConfigArgs = tags::UPDATE_VAULT_CONFIG,
466    InitializeAssetArgs = tags::INITIALIZE_ASSET,
467    UpdateAssetArgs = tags::UPDATE_ASSET,
468    SetPauseFlagsArgs = tags::SET_PAUSE_FLAGS,
469    SetVaultAccessArgs = tags::SET_VAULT_ACCESS,
470    TransferProgramAuthorityArgs = tags::TRANSFER_PROGRAM_AUTHORITY,
471    TransferVaultAuthorityArgs = tags::TRANSFER_VAULT_AUTHORITY,
472    SetStrategistArgs = tags::SET_STRATEGIST,
473    SetNavAuthorityArgs = tags::SET_NAV_AUTHORITY,
474    SetWithdrawalAuthorityArgs = tags::SET_WITHDRAWAL_AUTHORITY,
475    CollectFeesArgs = tags::COLLECT_FEES,
476    InvestExternalArgs = tags::INVEST_EXTERNAL,
477    ReturnExternalArgs = tags::RETURN_EXTERNAL,
478    SetSwapAuthorityArgs = tags::SET_SWAP_AUTHORITY,
479    AtomicRedeemArgs = tags::ATOMIC_REDEEM,
480    SwapArgs = tags::SWAP,
481    WriteDownFeesArgs = tags::WRITE_DOWN_FEES,
482    RegisterExternalDestinationArgs = tags::REGISTER_EXTERNAL_DESTINATION,
483    RevokeExternalDestinationArgs = tags::REVOKE_EXTERNAL_DESTINATION,
484    SetShareMetadataArgs = tags::SET_SHARE_METADATA,
485    AssertDelegateClearedArgs = tags::ASSERT_DELEGATE_CLEARED,
486    AdminSetFlashFeeRateArgs = tags::ADMIN_SET_FLASH_FEE_RATE,
487    StrategistLowerFlashFeeRateArgs = tags::STRATEGIST_LOWER_FLASH_FEE_RATE,
488}
489
490pub fn serialize_instruction<T>(args: &T) -> Result<Vec<u8>, wincode::WriteError>
491where
492    T: InstructionArgs,
493{
494    let mut data = vec![T::TAG];
495    wincode::serialize_into(&mut data, args)?;
496    Ok(data)
497}
498
499#[cfg(test)]
500mod tests {
501    use super::*;
502    use codama::{Codama, NodeTrait};
503    use serde_json::Value;
504    use std::path::Path;
505    use wincode::deserialize_exact;
506
507    #[test]
508    fn instruction_args_tags_match_canonical_tags() {
509        assert_eq!(
510            TAG_CASES,
511            &[
512                0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
513                23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33
514            ]
515        );
516        assert_eq!(
517            RoshiInstruction::ProcessWithdrawals.tag(),
518            <ProcessWithdrawalsArgs as InstructionArgs>::TAG
519        );
520    }
521
522    #[test]
523    fn codama_idl_uses_canonical_instruction_discriminators() {
524        let mut idl = Codama::load(Path::new(env!("CARGO_MANIFEST_DIR")))
525            .unwrap()
526            .get_idl()
527            .unwrap();
528        idl.program.name = "roshi".into();
529        let idl: Value = serde_json::from_str(&idl.to_json().unwrap()).unwrap();
530        let instructions = idl["program"]["instructions"].as_array().unwrap();
531
532        assert_eq!(idl["program"]["name"], "roshi");
533        assert_eq!(
534            idl["program"]["publicKey"],
535            "Roshi11111111111111111111111111111111111111"
536        );
537        assert_eq!(instructions.len(), TAG_CASES.len());
538
539        for (name, tag) in IDL_TAG_CASES {
540            assert_instruction_discriminator(instructions, name, *tag);
541        }
542
543        let deposit = instruction(instructions, "deposit");
544        assert_eq!(deposit["arguments"][1]["name"], "args");
545        assert_eq!(deposit["arguments"][1]["type"]["name"], "depositArgs");
546
547        let process_withdrawals = instruction(instructions, "processWithdrawals");
548        assert_eq!(
549            process_withdrawals["arguments"].as_array().unwrap().len(),
550            1
551        );
552    }
553
554    #[test]
555    fn instruction_decode_rejects_unknown_values() {
556        assert!(RoshiInstruction::decode(&[255]).is_err());
557    }
558
559    #[test]
560    fn serialize_instruction_writes_tag_then_args_payload() {
561        let args = DepositArgs {
562            asset_mint: [4; 32],
563            amount: 123,
564            min_shares_out: 456,
565            access_proof: vec![[1; 32], [2; 32], [3; 32]],
566        };
567
568        let encoded = serialize_instruction(&args).unwrap();
569        let decoded: DepositArgs = deserialize_exact(&encoded[1..]).unwrap();
570
571        assert_eq!(encoded[0], <DepositArgs as InstructionArgs>::TAG);
572        assert_eq!(decoded.asset_mint, [4; 32]);
573        assert_eq!(decoded.amount, 123);
574        assert_eq!(decoded.min_shares_out, 456);
575        assert_eq!(decoded.access_proof, vec![[1; 32], [2; 32], [3; 32]]);
576    }
577
578    #[test]
579    fn canonical_instruction_serializes_like_args_helper() {
580        let args = DepositArgs {
581            asset_mint: [4; 32],
582            amount: 123,
583            min_shares_out: 456,
584            access_proof: vec![[1; 32]],
585        };
586
587        assert_eq!(
588            RoshiInstruction::Deposit(args).serialize().unwrap(),
589            serialize_instruction(&DepositArgs {
590                asset_mint: [4; 32],
591                amount: 123,
592                min_shares_out: 456,
593                access_proof: vec![[1; 32]],
594            })
595            .unwrap()
596        );
597    }
598
599    #[test]
600    fn serialize_zero_sized_args_writes_only_tag() {
601        assert_eq!(
602            serialize_instruction(&ProcessWithdrawalsArgs).unwrap(),
603            vec![<ProcessWithdrawalsArgs as InstructionArgs>::TAG]
604        );
605        assert_eq!(
606            RoshiInstruction::ProcessWithdrawals.serialize().unwrap(),
607            vec![<ProcessWithdrawalsArgs as InstructionArgs>::TAG]
608        );
609    }
610
611    fn instruction<'a>(instructions: &'a [Value], name: &str) -> &'a Value {
612        instructions
613            .iter()
614            .find(|instruction| instruction["name"] == name)
615            .unwrap()
616    }
617
618    fn assert_instruction_discriminator(instructions: &[Value], name: &str, tag: u8) {
619        assert_eq!(
620            instruction(instructions, name)["arguments"][0]["defaultValue"]["number"],
621            u64::from(tag)
622        );
623    }
624
625    const IDL_TAG_CASES: &[(&str, u8)] = &[
626        ("initializeProgram", tags::INITIALIZE_PROGRAM),
627        ("initializeVault", tags::INITIALIZE_VAULT),
628        ("authorizeAction", tags::AUTHORIZE_ACTION),
629        ("revokeAction", tags::REVOKE_ACTION),
630        ("manage", tags::MANAGE),
631        ("manageBatch", tags::MANAGE_BATCH),
632        ("reportNav", tags::REPORT_NAV),
633        ("deposit", tags::DEPOSIT),
634        ("redeem", tags::REDEEM),
635        ("cancelRedeem", tags::CANCEL_REDEEM),
636        ("processWithdrawals", tags::PROCESS_WITHDRAWALS),
637        ("updateVaultConfig", tags::UPDATE_VAULT_CONFIG),
638        ("initializeAsset", tags::INITIALIZE_ASSET),
639        ("updateAsset", tags::UPDATE_ASSET),
640        ("setPauseFlags", tags::SET_PAUSE_FLAGS),
641        ("setVaultAccess", tags::SET_VAULT_ACCESS),
642        ("transferProgramAuthority", tags::TRANSFER_PROGRAM_AUTHORITY),
643        ("transferVaultAuthority", tags::TRANSFER_VAULT_AUTHORITY),
644        ("setStrategist", tags::SET_STRATEGIST),
645        ("setNavAuthority", tags::SET_NAV_AUTHORITY),
646        ("setWithdrawalAuthority", tags::SET_WITHDRAWAL_AUTHORITY),
647        ("collectFees", tags::COLLECT_FEES),
648        ("investExternal", tags::INVEST_EXTERNAL),
649        ("returnExternal", tags::RETURN_EXTERNAL),
650        ("setSwapAuthority", tags::SET_SWAP_AUTHORITY),
651        ("atomicRedeem", tags::ATOMIC_REDEEM),
652        ("swap", tags::SWAP),
653        ("writeDownFees", tags::WRITE_DOWN_FEES),
654        (
655            "registerExternalDestination",
656            tags::REGISTER_EXTERNAL_DESTINATION,
657        ),
658        (
659            "revokeExternalDestination",
660            tags::REVOKE_EXTERNAL_DESTINATION,
661        ),
662        ("setShareMetadata", tags::SET_SHARE_METADATA),
663        ("assertDelegateCleared", tags::ASSERT_DELEGATE_CLEARED),
664        ("adminSetFlashFeeRate", tags::ADMIN_SET_FLASH_FEE_RATE),
665        (
666            "strategistLowerFlashFeeRate",
667            tags::STRATEGIST_LOWER_FLASH_FEE_RATE,
668        ),
669    ];
670}