cpi.rs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. use crate::codegen::program::common::{generate_ix_variant, sighash, SIGHASH_GLOBAL_NAMESPACE};
  2. use crate::Program;
  3. use heck::SnakeCase;
  4. use quote::{quote, ToTokens};
  5. pub fn generate(program: &Program) -> proc_macro2::TokenStream {
  6. // Generate cpi methods for global methods.
  7. let global_cpi_methods: Vec<proc_macro2::TokenStream> = program
  8. .ixs
  9. .iter()
  10. .map(|ix| {
  11. let accounts_ident: proc_macro2::TokenStream = format!("crate::cpi::accounts::{}", &ix.anchor_ident.to_string()).parse().unwrap();
  12. let cpi_method = {
  13. let ix_variant = generate_ix_variant(ix.raw_method.sig.ident.to_string(), &ix.args);
  14. let method_name = &ix.ident;
  15. let args: Vec<&syn::PatType> = ix.args.iter().map(|arg| &arg.raw_arg).collect();
  16. let name = &ix.raw_method.sig.ident.to_string();
  17. let sighash_arr = sighash(SIGHASH_GLOBAL_NAMESPACE, name);
  18. let sighash_tts: proc_macro2::TokenStream =
  19. format!("{sighash_arr:?}").parse().unwrap();
  20. let ret_type = &ix.returns.ty.to_token_stream();
  21. let (method_ret, maybe_return) = match ret_type.to_string().as_str() {
  22. "()" => (quote! {anchor_lang::Result<()> }, quote! { Ok(()) }),
  23. _ => (
  24. quote! { anchor_lang::Result<crate::cpi::Return::<#ret_type>> },
  25. quote! { Ok(crate::cpi::Return::<#ret_type> { phantom: crate::cpi::PhantomData }) }
  26. )
  27. };
  28. quote! {
  29. pub fn #method_name<'a, 'b, 'c, 'info>(
  30. ctx: anchor_lang::context::CpiContext<'a, 'b, 'c, 'info, #accounts_ident<'info>>,
  31. #(#args),*
  32. ) -> #method_ret {
  33. let ix = {
  34. let ix = instruction::#ix_variant;
  35. let mut ix_data = AnchorSerialize::try_to_vec(&ix)
  36. .map_err(|_| anchor_lang::error::ErrorCode::InstructionDidNotSerialize)?;
  37. let mut data = #sighash_tts.to_vec();
  38. data.append(&mut ix_data);
  39. let accounts = ctx.to_account_metas(None);
  40. anchor_lang::solana_program::instruction::Instruction {
  41. program_id: ctx.program.key(),
  42. accounts,
  43. data,
  44. }
  45. };
  46. let mut acc_infos = ctx.to_account_infos();
  47. anchor_lang::solana_program::program::invoke_signed(
  48. &ix,
  49. &acc_infos,
  50. ctx.signer_seeds,
  51. ).map_or_else(
  52. |e| Err(Into::into(e)),
  53. // Maybe handle Solana return data.
  54. |_| { #maybe_return }
  55. )
  56. }
  57. }
  58. };
  59. cpi_method
  60. })
  61. .collect();
  62. let accounts = generate_accounts(program);
  63. quote! {
  64. #[cfg(feature = "cpi")]
  65. pub mod cpi {
  66. use super::*;
  67. use std::marker::PhantomData;
  68. pub struct Return<T> {
  69. phantom: std::marker::PhantomData<T>
  70. }
  71. impl<T: AnchorDeserialize> Return<T> {
  72. pub fn get(&self) -> T {
  73. let (_key, data) = anchor_lang::solana_program::program::get_return_data().unwrap();
  74. T::try_from_slice(&data).unwrap()
  75. }
  76. }
  77. #(#global_cpi_methods)*
  78. #accounts
  79. }
  80. }
  81. }
  82. pub fn generate_accounts(program: &Program) -> proc_macro2::TokenStream {
  83. let mut accounts = std::collections::HashSet::new();
  84. // Go through instruction accounts.
  85. for ix in &program.ixs {
  86. let anchor_ident = &ix.anchor_ident;
  87. // TODO: move to fn and share with accounts.rs.
  88. let macro_name = format!(
  89. "__cpi_client_accounts_{}",
  90. anchor_ident.to_string().to_snake_case()
  91. );
  92. accounts.insert(macro_name);
  93. }
  94. // Build the tokens from all accounts
  95. let account_structs: Vec<proc_macro2::TokenStream> = accounts
  96. .iter()
  97. .map(|macro_name: &String| {
  98. let macro_name: proc_macro2::TokenStream = macro_name.parse().unwrap();
  99. quote! {
  100. pub use crate::#macro_name::*;
  101. }
  102. })
  103. .collect();
  104. quote! {
  105. /// An Anchor generated module, providing a set of structs
  106. /// mirroring the structs deriving `Accounts`, where each field is
  107. /// an `AccountInfo`. This is useful for CPI.
  108. pub mod accounts {
  109. #(#account_structs)*
  110. }
  111. }
  112. }