cpi.rs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. use crate::codegen::program::common::{generate_ix_variant, sighash, SIGHASH_GLOBAL_NAMESPACE};
  2. use crate::Program;
  3. use crate::StateIx;
  4. use quote::quote;
  5. pub fn generate(program: &Program) -> proc_macro2::TokenStream {
  6. // Generate cpi methods for the state struct.
  7. // The Ctor is not exposed via CPI, since it is a one time use function.
  8. let state_cpi_methods: Vec<proc_macro2::TokenStream> = program
  9. .state
  10. .as_ref()
  11. .map(|state| {
  12. state
  13. .impl_block_and_methods
  14. .as_ref()
  15. .map(|(_, methods)| {
  16. methods
  17. .iter()
  18. .map(|method: &StateIx| {
  19. let accounts_ident = &method.anchor_ident;
  20. let ix_variant = generate_ix_variant(
  21. method.raw_method.sig.ident.to_string(),
  22. &method.args,
  23. );
  24. let method_name = &method.ident;
  25. let args: Vec<&syn::PatType> =
  26. method.args.iter().map(|arg| &arg.raw_arg).collect();
  27. quote! {
  28. pub fn #method_name<'a, 'b, 'c, 'info>(
  29. ctx: CpiStateContext<'a, 'b, 'c, 'info, #accounts_ident<'info>>,
  30. #(#args),*
  31. ) -> ProgramResult {
  32. let ix = {
  33. let ix = instruction::state::#ix_variant;
  34. let data = anchor_lang::InstructionData::data(&ix);
  35. let accounts = ctx.to_account_metas(None);
  36. anchor_lang::solana_program::instruction::Instruction {
  37. program_id: *ctx.program().key,
  38. accounts,
  39. data,
  40. }
  41. };
  42. let mut acc_infos = ctx.to_account_infos();
  43. anchor_lang::solana_program::program::invoke_signed(
  44. &ix,
  45. &acc_infos,
  46. ctx.signer_seeds(),
  47. )
  48. }
  49. }
  50. })
  51. .collect()
  52. })
  53. .unwrap_or(vec![])
  54. })
  55. .unwrap_or(vec![]);
  56. // Generate cpi methods for global methods.
  57. let global_cpi_methods: Vec<proc_macro2::TokenStream> = program
  58. .ixs
  59. .iter()
  60. .map(|ix| {
  61. let accounts_ident = &ix.anchor_ident;
  62. let cpi_method = {
  63. let ix_variant = generate_ix_variant(ix.raw_method.sig.ident.to_string(), &ix.args);
  64. let method_name = &ix.ident;
  65. let args: Vec<&syn::PatType> = ix.args.iter().map(|arg| &arg.raw_arg).collect();
  66. let name = &ix.raw_method.sig.ident.to_string();
  67. let sighash_arr = sighash(SIGHASH_GLOBAL_NAMESPACE, &name);
  68. let sighash_tts: proc_macro2::TokenStream =
  69. format!("{:?}", sighash_arr).parse().unwrap();
  70. quote! {
  71. pub fn #method_name<'a, 'b, 'c, 'info>(
  72. ctx: CpiContext<'a, 'b, 'c, 'info, #accounts_ident<'info>>,
  73. #(#args),*
  74. ) -> ProgramResult {
  75. let ix = {
  76. let ix = instruction::#ix_variant;
  77. let mut ix_data = AnchorSerialize::try_to_vec(&ix)
  78. .map_err(|_| anchor_lang::__private::ErrorCode::InstructionDidNotSerialize)?;
  79. let mut data = #sighash_tts.to_vec();
  80. data.append(&mut ix_data);
  81. let accounts = ctx.to_account_metas(None);
  82. anchor_lang::solana_program::instruction::Instruction {
  83. program_id: *ctx.program.key,
  84. accounts,
  85. data,
  86. }
  87. };
  88. let mut acc_infos = ctx.to_account_infos();
  89. acc_infos.push(ctx.program.clone());
  90. anchor_lang::solana_program::program::invoke_signed(
  91. &ix,
  92. &acc_infos,
  93. ctx.signer_seeds,
  94. )
  95. }
  96. }
  97. };
  98. cpi_method
  99. })
  100. .collect();
  101. quote! {
  102. #[cfg(feature = "cpi")]
  103. pub mod cpi {
  104. use super::*;
  105. pub mod state {
  106. use super::*;
  107. #(#state_cpi_methods)*
  108. }
  109. #(#global_cpi_methods)*
  110. }
  111. }
  112. }