forked from otter-sec/anchor
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcpi.rs
More file actions
116 lines (100 loc) · 3.69 KB
/
Copy pathcpi.rs
File metadata and controls
116 lines (100 loc) · 3.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use anchor_lang_idl::types::Idl;
use heck::CamelCase;
use quote::{format_ident, quote};
use super::common::{convert_idl_type_to_syn_type, gen_accounts_common, gen_discriminator};
pub fn gen_cpi_mod(idl: &Idl) -> proc_macro2::TokenStream {
let cpi_instructions = gen_cpi_instructions(idl);
let cpi_return_type = gen_cpi_return_type();
let cpi_accounts_mod = gen_cpi_accounts_mod(idl);
quote! {
/// Cross program invocation (CPI) helpers.
pub mod cpi {
use super::*;
#cpi_instructions
#cpi_return_type
#cpi_accounts_mod
}
}
}
fn gen_cpi_instructions(idl: &Idl) -> proc_macro2::TokenStream {
let ixs = idl.instructions.iter().map(|ix| {
let method_name = format_ident!("{}", ix.name);
let accounts_ident = format_ident!("{}", ix.name.to_camel_case());
let args = ix.args.iter().map(|arg| {
let name = format_ident!("{}", arg.name);
let ty = convert_idl_type_to_syn_type(&arg.ty);
quote! { #name: #ty }
});
let arg_value = if ix.args.is_empty() {
quote! { #accounts_ident }
} else {
let fields= ix.args.iter().map(|arg| format_ident!("{}", arg.name));
quote! {
#accounts_ident {
#(#fields),*
}
}
};
let discriminator = gen_discriminator(&ix.discriminator);
let (ret_type, ret_value) = match ix.returns.as_ref() {
Some(ty) => {
let ty = convert_idl_type_to_syn_type(ty);
(
quote! { anchor_lang::Result<Return::<#ty>> },
quote! { Ok(Return::<#ty> { phantom:: std::marker::PhantomData }) },
)
},
None => (
quote! { anchor_lang::Result<()> },
quote! { Ok(()) },
)
};
quote! {
pub fn #method_name<'a, 'b, 'c, 'info>(
ctx: anchor_lang::context::CpiContext<'a, 'b, 'c, 'info, accounts::#accounts_ident<'info>>,
#(#args),*
) -> #ret_type {
let ix = {
let mut data = Vec::with_capacity(256);
data.extend_from_slice(&#discriminator);
AnchorSerialize::serialize(&internal::args::#arg_value, &mut data)
.map_err(|_| anchor_lang::error::ErrorCode::InstructionDidNotSerialize)?;
let accounts = ctx.to_account_metas(None);
anchor_lang::solana_program::instruction::Instruction {
program_id: ctx.program.key(),
accounts,
data,
}
};
let mut acc_infos = ctx.to_account_infos();
anchor_lang::solana_program::program::invoke_signed(
&ix,
&acc_infos,
ctx.signer_seeds,
).map_or_else(
|e| Err(Into::into(e)),
|_| { #ret_value }
)
}
}
});
quote! {
#(#ixs)*
}
}
fn gen_cpi_return_type() -> proc_macro2::TokenStream {
quote! {
pub struct Return<T> {
phantom: std::marker::PhantomData<T>
}
impl<T: AnchorDeserialize> Return<T> {
pub fn get(&self) -> T {
let (_key, data) = anchor_lang::solana_program::program::get_return_data().unwrap();
T::try_from_slice(&data).unwrap()
}
}
}
}
fn gen_cpi_accounts_mod(idl: &Idl) -> proc_macro2::TokenStream {
gen_accounts_common(idl, "cpi_client")
}