constraints.rs 73 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677
  1. use quote::quote;
  2. use std::collections::HashSet;
  3. use crate::*;
  4. pub fn generate(f: &Field, accs: &AccountsStruct) -> proc_macro2::TokenStream {
  5. let constraints = linearize(&f.constraints);
  6. let rent = constraints
  7. .iter()
  8. .any(|c| matches!(c, Constraint::RentExempt(ConstraintRentExempt::Enforce)))
  9. .then(|| quote! { let __anchor_rent = Rent::get()?; })
  10. .unwrap_or_else(|| quote! {});
  11. let checks: Vec<proc_macro2::TokenStream> = constraints
  12. .iter()
  13. .map(|c| generate_constraint(f, c, accs))
  14. .collect();
  15. let mut all_checks = quote! {#(#checks)*};
  16. // If the field is optional we do all the inner checks as if the account
  17. // wasn't optional. If the account is init we also need to return an Option
  18. // by wrapping the resulting value with Some or returning None if it doesn't exist.
  19. if f.is_optional && !constraints.is_empty() {
  20. let ident = &f.ident;
  21. let ty_decl = f.ty_decl(false);
  22. all_checks = match &constraints[0] {
  23. Constraint::Init(_) | Constraint::Zeroed(_) => {
  24. quote! {
  25. let #ident: #ty_decl = if let Some(#ident) = #ident {
  26. #all_checks
  27. Some(#ident)
  28. } else {
  29. None
  30. };
  31. }
  32. }
  33. _ => {
  34. quote! {
  35. if let Some(#ident) = &#ident {
  36. #all_checks
  37. }
  38. }
  39. }
  40. };
  41. }
  42. quote! {
  43. #rent
  44. #all_checks
  45. }
  46. }
  47. pub fn generate_composite(f: &CompositeField) -> proc_macro2::TokenStream {
  48. let checks: Vec<proc_macro2::TokenStream> = linearize(&f.constraints)
  49. .iter()
  50. .map(|c| match c {
  51. Constraint::Raw(_) => c,
  52. _ => panic!("Invariant violation: composite constraints can only be raw or literals"),
  53. })
  54. .map(|c| generate_constraint_composite(f, c))
  55. .collect();
  56. quote! {
  57. #(#checks)*
  58. }
  59. }
  60. // Linearizes the constraint group so that constraints with dependencies
  61. // run after those without.
  62. pub fn linearize(c_group: &ConstraintGroup) -> Vec<Constraint> {
  63. let ConstraintGroup {
  64. init,
  65. zeroed,
  66. mutable,
  67. signer,
  68. has_one,
  69. raw,
  70. owner,
  71. rent_exempt,
  72. seeds,
  73. executable,
  74. close,
  75. address,
  76. associated_token,
  77. token_account,
  78. mint,
  79. realloc,
  80. } = c_group.clone();
  81. let mut constraints = Vec::new();
  82. if let Some(c) = zeroed {
  83. constraints.push(Constraint::Zeroed(c));
  84. }
  85. if let Some(c) = init {
  86. constraints.push(Constraint::Init(c));
  87. }
  88. if let Some(c) = realloc {
  89. constraints.push(Constraint::Realloc(c));
  90. }
  91. if let Some(c) = seeds {
  92. constraints.push(Constraint::Seeds(c));
  93. }
  94. if let Some(c) = associated_token {
  95. constraints.push(Constraint::AssociatedToken(c));
  96. }
  97. if let Some(c) = mutable {
  98. constraints.push(Constraint::Mut(c));
  99. }
  100. if let Some(c) = signer {
  101. constraints.push(Constraint::Signer(c));
  102. }
  103. constraints.append(&mut has_one.into_iter().map(Constraint::HasOne).collect());
  104. constraints.append(&mut raw.into_iter().map(Constraint::Raw).collect());
  105. if let Some(c) = owner {
  106. constraints.push(Constraint::Owner(c));
  107. }
  108. if let Some(c) = rent_exempt {
  109. constraints.push(Constraint::RentExempt(c));
  110. }
  111. if let Some(c) = executable {
  112. constraints.push(Constraint::Executable(c));
  113. }
  114. if let Some(c) = close {
  115. constraints.push(Constraint::Close(c));
  116. }
  117. if let Some(c) = address {
  118. constraints.push(Constraint::Address(c));
  119. }
  120. if let Some(c) = token_account {
  121. constraints.push(Constraint::TokenAccount(c));
  122. }
  123. if let Some(c) = mint {
  124. constraints.push(Constraint::Mint(c));
  125. }
  126. constraints
  127. }
  128. fn generate_constraint(
  129. f: &Field,
  130. c: &Constraint,
  131. accs: &AccountsStruct,
  132. ) -> proc_macro2::TokenStream {
  133. match c {
  134. Constraint::Init(c) => generate_constraint_init(f, c, accs),
  135. Constraint::Zeroed(c) => generate_constraint_zeroed(f, c),
  136. Constraint::Mut(c) => generate_constraint_mut(f, c),
  137. Constraint::HasOne(c) => generate_constraint_has_one(f, c, accs),
  138. Constraint::Signer(c) => generate_constraint_signer(f, c),
  139. Constraint::Raw(c) => generate_constraint_raw(&f.ident, c),
  140. Constraint::Owner(c) => generate_constraint_owner(f, c),
  141. Constraint::RentExempt(c) => generate_constraint_rent_exempt(f, c),
  142. Constraint::Seeds(c) => generate_constraint_seeds(f, c),
  143. Constraint::Executable(c) => generate_constraint_executable(f, c),
  144. Constraint::Close(c) => generate_constraint_close(f, c, accs),
  145. Constraint::Address(c) => generate_constraint_address(f, c),
  146. Constraint::AssociatedToken(c) => generate_constraint_associated_token(f, c, accs),
  147. Constraint::TokenAccount(c) => generate_constraint_token_account(f, c, accs),
  148. Constraint::Mint(c) => generate_constraint_mint(f, c, accs),
  149. Constraint::Realloc(c) => generate_constraint_realloc(f, c, accs),
  150. }
  151. }
  152. fn generate_constraint_composite(f: &CompositeField, c: &Constraint) -> proc_macro2::TokenStream {
  153. match c {
  154. Constraint::Raw(c) => generate_constraint_raw(&f.ident, c),
  155. _ => panic!("Invariant violation"),
  156. }
  157. }
  158. fn generate_constraint_address(f: &Field, c: &ConstraintAddress) -> proc_macro2::TokenStream {
  159. let field = &f.ident;
  160. let addr = &c.address;
  161. let error = generate_custom_error(
  162. field,
  163. &c.error,
  164. quote! { ConstraintAddress },
  165. &Some(&(quote! { actual }, quote! { expected })),
  166. );
  167. quote! {
  168. {
  169. let actual = #field.key();
  170. let expected = #addr;
  171. if actual != expected {
  172. return #error;
  173. }
  174. }
  175. }
  176. }
  177. pub fn generate_constraint_init(
  178. f: &Field,
  179. c: &ConstraintInitGroup,
  180. accs: &AccountsStruct,
  181. ) -> proc_macro2::TokenStream {
  182. generate_constraint_init_group(f, c, accs)
  183. }
  184. pub fn generate_constraint_zeroed(f: &Field, _c: &ConstraintZeroed) -> proc_macro2::TokenStream {
  185. let field = &f.ident;
  186. let name_str = field.to_string();
  187. let ty_decl = f.ty_decl(true);
  188. let from_account_info = f.from_account_info(None, false);
  189. quote! {
  190. let #field: #ty_decl = {
  191. let mut __data: &[u8] = &#field.try_borrow_data()?;
  192. let mut __disc_bytes = [0u8; 8];
  193. __disc_bytes.copy_from_slice(&__data[..8]);
  194. let __discriminator = u64::from_le_bytes(__disc_bytes);
  195. if __discriminator != 0 {
  196. return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintZero).with_account_name(#name_str));
  197. }
  198. #from_account_info
  199. };
  200. }
  201. }
  202. pub fn generate_constraint_close(
  203. f: &Field,
  204. c: &ConstraintClose,
  205. accs: &AccountsStruct,
  206. ) -> proc_macro2::TokenStream {
  207. let field = &f.ident;
  208. let name_str = field.to_string();
  209. let target = &c.sol_dest;
  210. let target_optional_check =
  211. OptionalCheckScope::new_with_field(accs, field).generate_check(target);
  212. quote! {
  213. {
  214. #target_optional_check
  215. if #field.key() == #target.key() {
  216. return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintClose).with_account_name(#name_str));
  217. }
  218. }
  219. }
  220. }
  221. pub fn generate_constraint_mut(f: &Field, c: &ConstraintMut) -> proc_macro2::TokenStream {
  222. let ident = &f.ident;
  223. let account_ref = generate_account_ref(f);
  224. let error = generate_custom_error(ident, &c.error, quote! { ConstraintMut }, &None);
  225. quote! {
  226. if !#account_ref.is_writable {
  227. return #error;
  228. }
  229. }
  230. }
  231. pub fn generate_constraint_has_one(
  232. f: &Field,
  233. c: &ConstraintHasOne,
  234. accs: &AccountsStruct,
  235. ) -> proc_macro2::TokenStream {
  236. let target = &c.join_target;
  237. let ident = &f.ident;
  238. let field = match &f.ty {
  239. Ty::AccountLoader(_) => quote! {#ident.load()?},
  240. _ => quote! {#ident},
  241. };
  242. let error = generate_custom_error(
  243. ident,
  244. &c.error,
  245. quote! { ConstraintHasOne },
  246. &Some(&(quote! { my_key }, quote! { target_key })),
  247. );
  248. let target_optional_check =
  249. OptionalCheckScope::new_with_field(accs, &field).generate_check(target);
  250. quote! {
  251. {
  252. #target_optional_check
  253. let my_key = #field.#target;
  254. let target_key = #target.key();
  255. if my_key != target_key {
  256. return #error;
  257. }
  258. }
  259. }
  260. }
  261. pub fn generate_constraint_signer(f: &Field, c: &ConstraintSigner) -> proc_macro2::TokenStream {
  262. let ident = &f.ident;
  263. let account_ref = generate_account_ref(f);
  264. let error = generate_custom_error(ident, &c.error, quote! { ConstraintSigner }, &None);
  265. quote! {
  266. if !#account_ref.is_signer {
  267. return #error;
  268. }
  269. }
  270. }
  271. pub fn generate_constraint_raw(ident: &Ident, c: &ConstraintRaw) -> proc_macro2::TokenStream {
  272. let raw = &c.raw;
  273. let error = generate_custom_error(ident, &c.error, quote! { ConstraintRaw }, &None);
  274. quote! {
  275. if !(#raw) {
  276. return #error;
  277. }
  278. }
  279. }
  280. pub fn generate_constraint_owner(f: &Field, c: &ConstraintOwner) -> proc_macro2::TokenStream {
  281. let ident = &f.ident;
  282. let maybe_deref = match &f.ty {
  283. Ty::Account(AccountTy { boxed, .. })
  284. | Ty::InterfaceAccount(InterfaceAccountTy { boxed, .. }) => *boxed,
  285. _ => false,
  286. }
  287. .then(|| quote!(*))
  288. .unwrap_or_default();
  289. let owner_address = &c.owner_address;
  290. let error = generate_custom_error(
  291. ident,
  292. &c.error,
  293. quote! { ConstraintOwner },
  294. &Some(&(quote! { *my_owner }, quote! { owner_address })),
  295. );
  296. quote! {
  297. {
  298. let my_owner = AsRef::<AccountInfo>::as_ref(& #maybe_deref #ident).owner;
  299. let owner_address = #owner_address;
  300. if my_owner != &owner_address {
  301. return #error;
  302. }
  303. }
  304. }
  305. }
  306. pub fn generate_constraint_rent_exempt(
  307. f: &Field,
  308. c: &ConstraintRentExempt,
  309. ) -> proc_macro2::TokenStream {
  310. let ident = &f.ident;
  311. let name_str = ident.to_string();
  312. let info = quote! {
  313. #ident.to_account_info()
  314. };
  315. match c {
  316. ConstraintRentExempt::Skip => quote! {},
  317. ConstraintRentExempt::Enforce => quote! {
  318. if !__anchor_rent.is_exempt(#info.lamports(), #info.try_data_len()?) {
  319. return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintRentExempt).with_account_name(#name_str));
  320. }
  321. },
  322. }
  323. }
  324. fn generate_constraint_realloc(
  325. f: &Field,
  326. c: &ConstraintReallocGroup,
  327. accs: &AccountsStruct,
  328. ) -> proc_macro2::TokenStream {
  329. let field = &f.ident;
  330. let account_name = field.to_string();
  331. let new_space = &c.space;
  332. let payer = &c.payer;
  333. let zero = &c.zero;
  334. let mut optional_check_scope = OptionalCheckScope::new_with_field(accs, field);
  335. let payer_optional_check = optional_check_scope.generate_check(payer);
  336. let system_program_optional_check =
  337. optional_check_scope.generate_check(quote! {system_program});
  338. quote! {
  339. // Blocks duplicate account reallocs in a single instruction to prevent accidental account overwrites
  340. // and to ensure the calculation of the change in bytes is based on account size at program entry
  341. // which inheritantly guarantee idempotency.
  342. if __reallocs.contains(&#field.key()) {
  343. return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::AccountDuplicateReallocs).with_account_name(#account_name));
  344. }
  345. let __anchor_rent = anchor_lang::prelude::Rent::get()?;
  346. let __field_info = #field.to_account_info();
  347. let __new_rent_minimum = __anchor_rent.minimum_balance(#new_space);
  348. let __delta_space = (::std::convert::TryInto::<isize>::try_into(#new_space).unwrap())
  349. .checked_sub(::std::convert::TryInto::try_into(__field_info.data_len()).unwrap())
  350. .unwrap();
  351. if __delta_space != 0 {
  352. #payer_optional_check
  353. if __delta_space > 0 {
  354. #system_program_optional_check
  355. if ::std::convert::TryInto::<usize>::try_into(__delta_space).unwrap() > anchor_lang::solana_program::entrypoint::MAX_PERMITTED_DATA_INCREASE {
  356. return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::AccountReallocExceedsLimit).with_account_name(#account_name));
  357. }
  358. if __new_rent_minimum > __field_info.lamports() {
  359. anchor_lang::system_program::transfer(
  360. anchor_lang::context::CpiContext::new(
  361. system_program.to_account_info(),
  362. anchor_lang::system_program::Transfer {
  363. from: #payer.to_account_info(),
  364. to: __field_info.clone(),
  365. },
  366. ),
  367. __new_rent_minimum.checked_sub(__field_info.lamports()).unwrap(),
  368. )?;
  369. }
  370. } else {
  371. let __lamport_amt = __field_info.lamports().checked_sub(__new_rent_minimum).unwrap();
  372. **#payer.to_account_info().lamports.borrow_mut() = #payer.to_account_info().lamports().checked_add(__lamport_amt).unwrap();
  373. **__field_info.lamports.borrow_mut() = __field_info.lamports().checked_sub(__lamport_amt).unwrap();
  374. }
  375. __field_info.realloc(#new_space, #zero)?;
  376. __reallocs.insert(#field.key());
  377. }
  378. }
  379. }
  380. fn generate_constraint_init_group(
  381. f: &Field,
  382. c: &ConstraintInitGroup,
  383. accs: &AccountsStruct,
  384. ) -> proc_macro2::TokenStream {
  385. let field = &f.ident;
  386. let name_str = f.ident.to_string();
  387. let ty_decl = f.ty_decl(true);
  388. let if_needed = if c.if_needed {
  389. quote! {true}
  390. } else {
  391. quote! {false}
  392. };
  393. let space = &c.space;
  394. let payer = &c.payer;
  395. // Convert from account info to account context wrapper type.
  396. let from_account_info = f.from_account_info(Some(&c.kind), true);
  397. let from_account_info_unchecked = f.from_account_info(Some(&c.kind), false);
  398. let account_ref = generate_account_ref(f);
  399. // PDA bump seeds.
  400. let (find_pda, seeds_with_bump) = match &c.seeds {
  401. None => (quote! {}, quote! {}),
  402. Some(c) => {
  403. let seeds = &mut c.seeds.clone();
  404. // If the seeds came with a trailing comma, we need to chop it off
  405. // before we interpolate them below.
  406. if let Some(pair) = seeds.pop() {
  407. seeds.push_value(pair.into_value());
  408. }
  409. let maybe_seeds_plus_comma = (!seeds.is_empty()).then(|| {
  410. quote! { #seeds, }
  411. });
  412. let validate_pda = {
  413. // If the bump is provided with init *and target*, then force it to be the
  414. // canonical bump.
  415. //
  416. // Note that for `#[account(init, seeds)]`, find_program_address has already
  417. // been run in the init constraint find_pda variable.
  418. if c.bump.is_some() {
  419. let b = c.bump.as_ref().unwrap();
  420. quote! {
  421. if #field.key() != __pda_address {
  422. return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str).with_pubkeys((#field.key(), __pda_address)));
  423. }
  424. if __bump != #b {
  425. return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str).with_values((__bump, #b)));
  426. }
  427. }
  428. } else {
  429. // Init seeds but no bump. We already used the canonical to create bump so
  430. // just check the address.
  431. //
  432. // Note that for `#[account(init, seeds)]`, find_program_address has already
  433. // been run in the init constraint find_pda variable.
  434. quote! {
  435. if #field.key() != __pda_address {
  436. return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str).with_pubkeys((#field.key(), __pda_address)));
  437. }
  438. }
  439. }
  440. };
  441. let bump = if f.is_optional {
  442. quote!(Some(__bump))
  443. } else {
  444. quote!(__bump)
  445. };
  446. (
  447. quote! {
  448. let (__pda_address, __bump) = Pubkey::find_program_address(
  449. &[#maybe_seeds_plus_comma],
  450. __program_id,
  451. );
  452. __bumps.#field = #bump;
  453. #validate_pda
  454. },
  455. quote! {
  456. &[
  457. #maybe_seeds_plus_comma
  458. &[__bump][..]
  459. ][..]
  460. },
  461. )
  462. }
  463. };
  464. // Optional check idents
  465. let system_program = &quote! {system_program};
  466. let associated_token_program = &quote! {associated_token_program};
  467. let rent = &quote! {rent};
  468. let mut check_scope = OptionalCheckScope::new_with_field(accs, field);
  469. match &c.kind {
  470. InitKind::Token {
  471. owner,
  472. mint,
  473. token_program,
  474. } => {
  475. let token_program = match token_program {
  476. Some(t) => t.to_token_stream(),
  477. None => quote! {token_program},
  478. };
  479. let owner_optional_check = check_scope.generate_check(owner);
  480. let mint_optional_check = check_scope.generate_check(mint);
  481. let system_program_optional_check = check_scope.generate_check(system_program);
  482. let token_program_optional_check = check_scope.generate_check(&token_program);
  483. let rent_optional_check = check_scope.generate_check(rent);
  484. let optional_checks = quote! {
  485. #system_program_optional_check
  486. #token_program_optional_check
  487. #rent_optional_check
  488. #owner_optional_check
  489. #mint_optional_check
  490. };
  491. let payer_optional_check = check_scope.generate_check(payer);
  492. let token_account_space = generate_get_token_account_space(mint);
  493. let create_account = generate_create_account(
  494. field,
  495. quote! {#token_account_space},
  496. quote! {&#token_program.key()},
  497. quote! {#payer},
  498. seeds_with_bump,
  499. );
  500. quote! {
  501. // Define the bump and pda variable.
  502. #find_pda
  503. let #field: #ty_decl = {
  504. // Checks that all the required accounts for this operation are present.
  505. #optional_checks
  506. let owner_program = #account_ref.owner;
  507. if !#if_needed || owner_program == &anchor_lang::solana_program::system_program::ID {
  508. #payer_optional_check
  509. // Create the account with the system program.
  510. #create_account
  511. // Initialize the token account.
  512. let cpi_program = #token_program.to_account_info();
  513. let accounts = ::anchor_spl::token_interface::InitializeAccount3 {
  514. account: #field.to_account_info(),
  515. mint: #mint.to_account_info(),
  516. authority: #owner.to_account_info(),
  517. };
  518. let cpi_ctx = anchor_lang::context::CpiContext::new(cpi_program, accounts);
  519. ::anchor_spl::token_interface::initialize_account3(cpi_ctx)?;
  520. }
  521. let pa: #ty_decl = #from_account_info_unchecked;
  522. if #if_needed {
  523. if pa.mint != #mint.key() {
  524. return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintTokenMint).with_account_name(#name_str).with_pubkeys((pa.mint, #mint.key())));
  525. }
  526. if pa.owner != #owner.key() {
  527. return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintTokenOwner).with_account_name(#name_str).with_pubkeys((pa.owner, #owner.key())));
  528. }
  529. if owner_program != &#token_program.key() {
  530. return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintTokenTokenProgram).with_account_name(#name_str).with_pubkeys((*owner_program, #token_program.key())));
  531. }
  532. }
  533. pa
  534. };
  535. }
  536. }
  537. InitKind::AssociatedToken {
  538. owner,
  539. mint,
  540. token_program,
  541. } => {
  542. let token_program = match token_program {
  543. Some(t) => t.to_token_stream(),
  544. None => quote! {token_program},
  545. };
  546. let owner_optional_check = check_scope.generate_check(owner);
  547. let mint_optional_check = check_scope.generate_check(mint);
  548. let system_program_optional_check = check_scope.generate_check(system_program);
  549. let token_program_optional_check = check_scope.generate_check(&token_program);
  550. let associated_token_program_optional_check =
  551. check_scope.generate_check(associated_token_program);
  552. let rent_optional_check = check_scope.generate_check(rent);
  553. let optional_checks = quote! {
  554. #system_program_optional_check
  555. #token_program_optional_check
  556. #associated_token_program_optional_check
  557. #rent_optional_check
  558. #owner_optional_check
  559. #mint_optional_check
  560. };
  561. let payer_optional_check = check_scope.generate_check(payer);
  562. quote! {
  563. // Define the bump and pda variable.
  564. #find_pda
  565. let #field: #ty_decl = {
  566. // Checks that all the required accounts for this operation are present.
  567. #optional_checks
  568. let owner_program = #account_ref.owner;
  569. if !#if_needed || owner_program == &anchor_lang::solana_program::system_program::ID {
  570. #payer_optional_check
  571. ::anchor_spl::associated_token::create(
  572. anchor_lang::context::CpiContext::new(
  573. associated_token_program.to_account_info(),
  574. ::anchor_spl::associated_token::Create {
  575. payer: #payer.to_account_info(),
  576. associated_token: #field.to_account_info(),
  577. authority: #owner.to_account_info(),
  578. mint: #mint.to_account_info(),
  579. system_program: system_program.to_account_info(),
  580. token_program: #token_program.to_account_info(),
  581. }
  582. )
  583. )?;
  584. }
  585. let pa: #ty_decl = #from_account_info_unchecked;
  586. if #if_needed {
  587. if pa.mint != #mint.key() {
  588. return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintTokenMint).with_account_name(#name_str).with_pubkeys((pa.mint, #mint.key())));
  589. }
  590. if pa.owner != #owner.key() {
  591. return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintTokenOwner).with_account_name(#name_str).with_pubkeys((pa.owner, #owner.key())));
  592. }
  593. if owner_program != &#token_program.key() {
  594. return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintAssociatedTokenTokenProgram).with_account_name(#name_str).with_pubkeys((*owner_program, #token_program.key())));
  595. }
  596. if pa.key() != ::anchor_spl::associated_token::get_associated_token_address_with_program_id(&#owner.key(), &#mint.key(), &#token_program.key()) {
  597. return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::AccountNotAssociatedTokenAccount).with_account_name(#name_str));
  598. }
  599. }
  600. pa
  601. };
  602. }
  603. }
  604. InitKind::Mint {
  605. owner,
  606. decimals,
  607. freeze_authority,
  608. token_program,
  609. group_pointer_authority,
  610. group_pointer_group_address,
  611. group_member_pointer_authority,
  612. group_member_pointer_member_address,
  613. metadata_pointer_authority,
  614. metadata_pointer_metadata_address,
  615. close_authority,
  616. permanent_delegate,
  617. transfer_hook_authority,
  618. transfer_hook_program_id,
  619. } => {
  620. let token_program = match token_program {
  621. Some(t) => t.to_token_stream(),
  622. None => quote! {token_program},
  623. };
  624. let owner_optional_check = check_scope.generate_check(owner);
  625. let freeze_authority_optional_check = match freeze_authority {
  626. Some(fa) => check_scope.generate_check(fa),
  627. None => quote! {},
  628. };
  629. // extension checks
  630. let group_pointer_authority_check = match group_pointer_authority {
  631. Some(gpa) => check_scope.generate_check(gpa),
  632. None => quote! {},
  633. };
  634. let group_pointer_group_address_check = match group_pointer_group_address {
  635. Some(gpga) => check_scope.generate_check(gpga),
  636. None => quote! {},
  637. };
  638. let group_member_pointer_authority_check = match group_member_pointer_authority {
  639. Some(gmpa) => check_scope.generate_check(gmpa),
  640. None => quote! {},
  641. };
  642. let group_member_pointer_member_address_check =
  643. match group_member_pointer_member_address {
  644. Some(gmpm) => check_scope.generate_check(gmpm),
  645. None => quote! {},
  646. };
  647. let metadata_pointer_authority_check = match metadata_pointer_authority {
  648. Some(mpa) => check_scope.generate_check(mpa),
  649. None => quote! {},
  650. };
  651. let metadata_pointer_metadata_address_check = match metadata_pointer_metadata_address {
  652. Some(mpma) => check_scope.generate_check(mpma),
  653. None => quote! {},
  654. };
  655. let close_authority_check = match close_authority {
  656. Some(ca) => check_scope.generate_check(ca),
  657. None => quote! {},
  658. };
  659. let transfer_hook_authority_check = match transfer_hook_authority {
  660. Some(tha) => check_scope.generate_check(tha),
  661. None => quote! {},
  662. };
  663. let transfer_hook_program_id_check = match transfer_hook_program_id {
  664. Some(thpid) => check_scope.generate_check(thpid),
  665. None => quote! {},
  666. };
  667. let permanent_delegate_check = match permanent_delegate {
  668. Some(pd) => check_scope.generate_check(pd),
  669. None => quote! {},
  670. };
  671. let system_program_optional_check = check_scope.generate_check(system_program);
  672. let token_program_optional_check = check_scope.generate_check(&token_program);
  673. let rent_optional_check = check_scope.generate_check(rent);
  674. let optional_checks = quote! {
  675. #system_program_optional_check
  676. #token_program_optional_check
  677. #rent_optional_check
  678. #owner_optional_check
  679. #freeze_authority_optional_check
  680. #group_pointer_authority_check
  681. #group_pointer_group_address_check
  682. #group_member_pointer_authority_check
  683. #group_member_pointer_member_address_check
  684. #metadata_pointer_authority_check
  685. #metadata_pointer_metadata_address_check
  686. #close_authority_check
  687. #transfer_hook_authority_check
  688. #transfer_hook_program_id_check
  689. #permanent_delegate_check
  690. };
  691. let payer_optional_check = check_scope.generate_check(payer);
  692. let mut extensions = vec![];
  693. if group_pointer_authority.is_some() || group_pointer_group_address.is_some() {
  694. extensions.push(quote! {::anchor_spl::token_interface::spl_token_2022::extension::ExtensionType::GroupPointer});
  695. }
  696. if group_member_pointer_authority.is_some()
  697. || group_member_pointer_member_address.is_some()
  698. {
  699. extensions.push(quote! {::anchor_spl::token_interface::spl_token_2022::extension::ExtensionType::GroupMemberPointer});
  700. }
  701. if metadata_pointer_authority.is_some() || metadata_pointer_metadata_address.is_some() {
  702. extensions.push(quote! {::anchor_spl::token_interface::spl_token_2022::extension::ExtensionType::MetadataPointer});
  703. }
  704. if close_authority.is_some() {
  705. extensions.push(quote! {::anchor_spl::token_interface::spl_token_2022::extension::ExtensionType::MintCloseAuthority});
  706. }
  707. if transfer_hook_authority.is_some() || transfer_hook_program_id.is_some() {
  708. extensions.push(quote! {::anchor_spl::token_interface::spl_token_2022::extension::ExtensionType::TransferHook});
  709. }
  710. if permanent_delegate.is_some() {
  711. extensions.push(quote! {::anchor_spl::token_interface::spl_token_2022::extension::ExtensionType::PermanentDelegate});
  712. }
  713. let mint_space = if extensions.is_empty() {
  714. quote! { ::anchor_spl::token::Mint::LEN }
  715. } else {
  716. quote! { ::anchor_spl::token_interface::find_mint_account_size(Some(&vec![#(#extensions),*]))? }
  717. };
  718. let extensions = if extensions.is_empty() {
  719. quote! {Option::<&::anchor_spl::token_interface::ExtensionsVec>::None}
  720. } else {
  721. quote! {Option::<&::anchor_spl::token_interface::ExtensionsVec>::Some(&vec![#(#extensions),*])}
  722. };
  723. let freeze_authority = match freeze_authority {
  724. Some(fa) => quote! { Option::<&anchor_lang::prelude::Pubkey>::Some(&#fa.key()) },
  725. None => quote! { Option::<&anchor_lang::prelude::Pubkey>::None },
  726. };
  727. let group_pointer_authority = match group_pointer_authority {
  728. Some(gpa) => quote! { Option::<anchor_lang::prelude::Pubkey>::Some(#gpa.key()) },
  729. None => quote! { Option::<anchor_lang::prelude::Pubkey>::None },
  730. };
  731. let group_pointer_group_address = match group_pointer_group_address {
  732. Some(gpga) => quote! { Option::<anchor_lang::prelude::Pubkey>::Some(#gpga.key()) },
  733. None => quote! { Option::<anchor_lang::prelude::Pubkey>::None },
  734. };
  735. let group_member_pointer_authority = match group_member_pointer_authority {
  736. Some(gmpa) => quote! { Option::<anchor_lang::prelude::Pubkey>::Some(#gmpa.key()) },
  737. None => quote! { Option::<anchor_lang::prelude::Pubkey>::None },
  738. };
  739. let group_member_pointer_member_address = match group_member_pointer_member_address {
  740. Some(gmpma) => {
  741. quote! { Option::<anchor_lang::prelude::Pubkey>::Some(#gmpma.key()) }
  742. }
  743. None => quote! { Option::<anchor_lang::prelude::Pubkey>::None },
  744. };
  745. let metadata_pointer_authority = match metadata_pointer_authority {
  746. Some(mpa) => quote! { Option::<anchor_lang::prelude::Pubkey>::Some(#mpa.key()) },
  747. None => quote! { Option::<anchor_lang::prelude::Pubkey>::None },
  748. };
  749. let metadata_pointer_metadata_address = match metadata_pointer_metadata_address {
  750. Some(mpma) => quote! { Option::<anchor_lang::prelude::Pubkey>::Some(#mpma.key()) },
  751. None => quote! { Option::<anchor_lang::prelude::Pubkey>::None },
  752. };
  753. let close_authority = match close_authority {
  754. Some(ca) => quote! { Option::<&anchor_lang::prelude::Pubkey>::Some(&#ca.key()) },
  755. None => quote! { Option::<&anchor_lang::prelude::Pubkey>::None },
  756. };
  757. let permanent_delegate = match permanent_delegate {
  758. Some(pd) => quote! { Option::<&anchor_lang::prelude::Pubkey>::Some(&#pd.key()) },
  759. None => quote! { Option::<&anchor_lang::prelude::Pubkey>::None },
  760. };
  761. let transfer_hook_authority = match transfer_hook_authority {
  762. Some(tha) => quote! { Option::<anchor_lang::prelude::Pubkey>::Some(#tha.key()) },
  763. None => quote! { Option::<anchor_lang::prelude::Pubkey>::None },
  764. };
  765. let transfer_hook_program_id = match transfer_hook_program_id {
  766. Some(thpid) => {
  767. quote! { Option::<anchor_lang::prelude::Pubkey>::Some(#thpid.key()) }
  768. }
  769. None => quote! { Option::<anchor_lang::prelude::Pubkey>::None },
  770. };
  771. let create_account = generate_create_account(
  772. field,
  773. mint_space,
  774. quote! {&#token_program.key()},
  775. quote! {#payer},
  776. seeds_with_bump,
  777. );
  778. quote! {
  779. // Define the bump and pda variable.
  780. #find_pda
  781. let #field: #ty_decl = {
  782. // Checks that all the required accounts for this operation are present.
  783. #optional_checks
  784. let owner_program = AsRef::<AccountInfo>::as_ref(&#field).owner;
  785. if !#if_needed || owner_program == &anchor_lang::solana_program::system_program::ID {
  786. // Define payer variable.
  787. #payer_optional_check
  788. // Create the account with the system program.
  789. #create_account
  790. // Initialize extensions.
  791. if let Some(extensions) = #extensions {
  792. for e in extensions {
  793. match e {
  794. ::anchor_spl::token_interface::spl_token_2022::extension::ExtensionType::GroupPointer => {
  795. ::anchor_spl::token_interface::group_pointer_initialize(anchor_lang::context::CpiContext::new(#token_program.to_account_info(), ::anchor_spl::token_interface::GroupPointerInitialize {
  796. token_program_id: #token_program.to_account_info(),
  797. mint: #field.to_account_info(),
  798. }), #group_pointer_authority, #group_pointer_group_address)?;
  799. },
  800. ::anchor_spl::token_interface::spl_token_2022::extension::ExtensionType::GroupMemberPointer => {
  801. ::anchor_spl::token_interface::group_member_pointer_initialize(anchor_lang::context::CpiContext::new(#token_program.to_account_info(), ::anchor_spl::token_interface::GroupMemberPointerInitialize {
  802. token_program_id: #token_program.to_account_info(),
  803. mint: #field.to_account_info(),
  804. }), #group_member_pointer_authority, #group_member_pointer_member_address)?;
  805. },
  806. ::anchor_spl::token_interface::spl_token_2022::extension::ExtensionType::MetadataPointer => {
  807. ::anchor_spl::token_interface::metadata_pointer_initialize(anchor_lang::context::CpiContext::new(#token_program.to_account_info(), ::anchor_spl::token_interface::MetadataPointerInitialize {
  808. token_program_id: #token_program.to_account_info(),
  809. mint: #field.to_account_info(),
  810. }), #metadata_pointer_authority, #metadata_pointer_metadata_address)?;
  811. },
  812. ::anchor_spl::token_interface::spl_token_2022::extension::ExtensionType::MintCloseAuthority => {
  813. ::anchor_spl::token_interface::mint_close_authority_initialize(anchor_lang::context::CpiContext::new(#token_program.to_account_info(), ::anchor_spl::token_interface::MintCloseAuthorityInitialize {
  814. token_program_id: #token_program.to_account_info(),
  815. mint: #field.to_account_info(),
  816. }), #close_authority)?;
  817. },
  818. ::anchor_spl::token_interface::spl_token_2022::extension::ExtensionType::TransferHook => {
  819. ::anchor_spl::token_interface::transfer_hook_initialize(anchor_lang::context::CpiContext::new(#token_program.to_account_info(), ::anchor_spl::token_interface::TransferHookInitialize {
  820. token_program_id: #token_program.to_account_info(),
  821. mint: #field.to_account_info(),
  822. }), #transfer_hook_authority, #transfer_hook_program_id)?;
  823. },
  824. ::anchor_spl::token_interface::spl_token_2022::extension::ExtensionType::NonTransferable => {
  825. ::anchor_spl::token_interface::non_transferable_mint_initialize(anchor_lang::context::CpiContext::new(#token_program.to_account_info(), ::anchor_spl::token_interface::NonTransferableMintInitialize {
  826. token_program_id: #token_program.to_account_info(),
  827. mint: #field.to_account_info(),
  828. }))?;
  829. },
  830. ::anchor_spl::token_interface::spl_token_2022::extension::ExtensionType::PermanentDelegate => {
  831. ::anchor_spl::token_interface::permanent_delegate_initialize(anchor_lang::context::CpiContext::new(#token_program.to_account_info(), ::anchor_spl::token_interface::PermanentDelegateInitialize {
  832. token_program_id: #token_program.to_account_info(),
  833. mint: #field.to_account_info(),
  834. }), #permanent_delegate.unwrap())?;
  835. },
  836. // All extensions specified by the user should be implemented.
  837. // If this line runs, it means there is a bug in the codegen.
  838. _ => unimplemented!("{e:?}"),
  839. }
  840. };
  841. }
  842. // Initialize the mint account.
  843. let cpi_program = #token_program.to_account_info();
  844. let accounts = ::anchor_spl::token_interface::InitializeMint2 {
  845. mint: #field.to_account_info(),
  846. };
  847. let cpi_ctx = anchor_lang::context::CpiContext::new(cpi_program, accounts);
  848. ::anchor_spl::token_interface::initialize_mint2(cpi_ctx, #decimals, &#owner.key(), #freeze_authority)?;
  849. }
  850. let pa: #ty_decl = #from_account_info_unchecked;
  851. if #if_needed {
  852. if pa.mint_authority != anchor_lang::solana_program::program_option::COption::Some(#owner.key()) {
  853. return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintMintMintAuthority).with_account_name(#name_str));
  854. }
  855. if pa.freeze_authority
  856. .as_ref()
  857. .map(|fa| #freeze_authority.as_ref().map(|expected_fa| fa != *expected_fa).unwrap_or(true))
  858. .unwrap_or(#freeze_authority.is_some()) {
  859. return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintMintFreezeAuthority).with_account_name(#name_str));
  860. }
  861. if pa.decimals != #decimals {
  862. return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintMintDecimals).with_account_name(#name_str).with_values((pa.decimals, #decimals)));
  863. }
  864. if owner_program != &#token_program.key() {
  865. return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintMintTokenProgram).with_account_name(#name_str).with_pubkeys((*owner_program, #token_program.key())));
  866. }
  867. }
  868. pa
  869. };
  870. }
  871. }
  872. InitKind::Program { owner } | InitKind::Interface { owner } => {
  873. // Define the space variable.
  874. let space = quote! {let space = #space;};
  875. let system_program_optional_check = check_scope.generate_check(system_program);
  876. // Define the owner of the account being created. If not specified,
  877. // default to the currently executing program.
  878. let (owner, owner_optional_check) = match owner {
  879. None => (
  880. quote! {
  881. __program_id
  882. },
  883. quote! {},
  884. ),
  885. Some(o) => {
  886. // We clone the `check_scope` here to avoid collisions with the
  887. // `payer_optional_check`, which is in a separate scope
  888. let owner_optional_check = check_scope.clone().generate_check(o);
  889. (
  890. quote! {
  891. &#o
  892. },
  893. owner_optional_check,
  894. )
  895. }
  896. };
  897. let payer_optional_check = check_scope.generate_check(payer);
  898. let optional_checks = quote! {
  899. #system_program_optional_check
  900. };
  901. // CPI to the system program to create the account.
  902. let create_account = generate_create_account(
  903. field,
  904. quote! {space},
  905. owner.clone(),
  906. quote! {#payer},
  907. seeds_with_bump,
  908. );
  909. // Put it all together.
  910. quote! {
  911. // Define the bump variable.
  912. #find_pda
  913. let #field = {
  914. // Checks that all the required accounts for this operation are present.
  915. #optional_checks
  916. let actual_field = #account_ref;
  917. let actual_owner = actual_field.owner;
  918. // Define the account space variable.
  919. #space
  920. // Create the account. Always do this in the event
  921. // if needed is not specified or the system program is the owner.
  922. let pa: #ty_decl = if !#if_needed || actual_owner == &anchor_lang::solana_program::system_program::ID {
  923. #payer_optional_check
  924. // CPI to the system program to create.
  925. #create_account
  926. // Convert from account info to account context wrapper type.
  927. #from_account_info_unchecked
  928. } else {
  929. // Convert from account info to account context wrapper type.
  930. #from_account_info
  931. };
  932. // Assert the account was created correctly.
  933. if #if_needed {
  934. #owner_optional_check
  935. if space != actual_field.data_len() {
  936. return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSpace).with_account_name(#name_str).with_values((space, actual_field.data_len())));
  937. }
  938. if actual_owner != #owner {
  939. return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintOwner).with_account_name(#name_str).with_pubkeys((*actual_owner, *#owner)));
  940. }
  941. {
  942. let required_lamports = __anchor_rent.minimum_balance(space);
  943. if pa.to_account_info().lamports() < required_lamports {
  944. return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintRentExempt).with_account_name(#name_str));
  945. }
  946. }
  947. }
  948. // Done.
  949. pa
  950. };
  951. }
  952. }
  953. }
  954. }
  955. fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2::TokenStream {
  956. if c.is_init {
  957. // Note that for `#[account(init, seeds)]`, the seed generation and checks is checked in
  958. // the init constraint find_pda/validate_pda block, so we don't do anything here and
  959. // return nothing!
  960. quote! {}
  961. } else {
  962. let name = &f.ident;
  963. let name_str = name.to_string();
  964. let s = &mut c.seeds.clone();
  965. let deriving_program_id = c
  966. .program_seed
  967. .clone()
  968. // If they specified a seeds::program to use when deriving the PDA, use it.
  969. .map(|program_id| quote! { #program_id.key() })
  970. // Otherwise fall back to the current program's program_id.
  971. .unwrap_or(quote! { __program_id });
  972. // If the seeds came with a trailing comma, we need to chop it off
  973. // before we interpolate them below.
  974. if let Some(pair) = s.pop() {
  975. s.push_value(pair.into_value());
  976. }
  977. let maybe_seeds_plus_comma = (!s.is_empty()).then(|| {
  978. quote! { #s, }
  979. });
  980. let bump = if f.is_optional {
  981. quote!(Some(__bump))
  982. } else {
  983. quote!(__bump)
  984. };
  985. // Not init here, so do all the checks.
  986. let define_pda = match c.bump.as_ref() {
  987. // Bump target not given. Find it.
  988. None => quote! {
  989. let (__pda_address, __bump) = Pubkey::find_program_address(
  990. &[#maybe_seeds_plus_comma],
  991. &#deriving_program_id,
  992. );
  993. __bumps.#name = #bump;
  994. },
  995. // Bump target given. Use it.
  996. Some(b) => quote! {
  997. let __pda_address = Pubkey::create_program_address(
  998. &[#maybe_seeds_plus_comma &[#b][..]],
  999. &#deriving_program_id,
  1000. ).map_err(|_| anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str))?;
  1001. },
  1002. };
  1003. quote! {
  1004. // Define the PDA.
  1005. #define_pda
  1006. // Check it.
  1007. if #name.key() != __pda_address {
  1008. return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str).with_pubkeys((#name.key(), __pda_address)));
  1009. }
  1010. }
  1011. }
  1012. }
  1013. fn generate_constraint_associated_token(
  1014. f: &Field,
  1015. c: &ConstraintAssociatedToken,
  1016. accs: &AccountsStruct,
  1017. ) -> proc_macro2::TokenStream {
  1018. let name = &f.ident;
  1019. let name_str = name.to_string();
  1020. let account_ref = generate_account_ref(f);
  1021. let wallet_address = &c.wallet;
  1022. let spl_token_mint_address = &c.mint;
  1023. let mut optional_check_scope = OptionalCheckScope::new_with_field(accs, name);
  1024. let wallet_address_optional_check = optional_check_scope.generate_check(wallet_address);
  1025. let spl_token_mint_address_optional_check =
  1026. optional_check_scope.generate_check(spl_token_mint_address);
  1027. let optional_checks = quote! {
  1028. #wallet_address_optional_check
  1029. #spl_token_mint_address_optional_check
  1030. };
  1031. let token_program_check = match &c.token_program {
  1032. Some(token_program) => {
  1033. let token_program_optional_check = optional_check_scope.generate_check(token_program);
  1034. quote! {
  1035. #token_program_optional_check
  1036. if #account_ref.owner != &#token_program.key() { return Err(anchor_lang::error::ErrorCode::ConstraintAssociatedTokenTokenProgram.into()); }
  1037. }
  1038. }
  1039. None => quote! {},
  1040. };
  1041. let get_associated_token_address = match &c.token_program {
  1042. Some(token_program) => quote! {
  1043. ::anchor_spl::associated_token::get_associated_token_address_with_program_id(&wallet_address, &#spl_token_mint_address.key(), &#token_program.key())
  1044. },
  1045. None => quote! {
  1046. ::anchor_spl::associated_token::get_associated_token_address(&wallet_address, &#spl_token_mint_address.key())
  1047. },
  1048. };
  1049. quote! {
  1050. {
  1051. #optional_checks
  1052. #token_program_check
  1053. let my_owner = #name.owner;
  1054. let wallet_address = #wallet_address.key();
  1055. if my_owner != wallet_address {
  1056. return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintTokenOwner).with_account_name(#name_str).with_pubkeys((my_owner, wallet_address)));
  1057. }
  1058. let __associated_token_address = #get_associated_token_address;
  1059. let my_key = #name.key();
  1060. if my_key != __associated_token_address {
  1061. return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintAssociated).with_account_name(#name_str).with_pubkeys((my_key, __associated_token_address)));
  1062. }
  1063. }
  1064. }
  1065. }
  1066. fn generate_constraint_token_account(
  1067. f: &Field,
  1068. c: &ConstraintTokenAccountGroup,
  1069. accs: &AccountsStruct,
  1070. ) -> proc_macro2::TokenStream {
  1071. let name = &f.ident;
  1072. let account_ref = generate_account_ref(f);
  1073. let mut optional_check_scope = OptionalCheckScope::new_with_field(accs, name);
  1074. let authority_check = match &c.authority {
  1075. Some(authority) => {
  1076. let authority_optional_check = optional_check_scope.generate_check(authority);
  1077. quote! {
  1078. #authority_optional_check
  1079. if #name.owner != #authority.key() { return Err(anchor_lang::error::ErrorCode::ConstraintTokenOwner.into()); }
  1080. }
  1081. }
  1082. None => quote! {},
  1083. };
  1084. let mint_check = match &c.mint {
  1085. Some(mint) => {
  1086. let mint_optional_check = optional_check_scope.generate_check(mint);
  1087. quote! {
  1088. #mint_optional_check
  1089. if #name.mint != #mint.key() { return Err(anchor_lang::error::ErrorCode::ConstraintTokenMint.into()); }
  1090. }
  1091. }
  1092. None => quote! {},
  1093. };
  1094. let token_program_check = match &c.token_program {
  1095. Some(token_program) => {
  1096. let token_program_optional_check = optional_check_scope.generate_check(token_program);
  1097. quote! {
  1098. #token_program_optional_check
  1099. if #account_ref.owner != &#token_program.key() { return Err(anchor_lang::error::ErrorCode::ConstraintTokenTokenProgram.into()); }
  1100. }
  1101. }
  1102. None => quote! {},
  1103. };
  1104. quote! {
  1105. {
  1106. #authority_check
  1107. #mint_check
  1108. #token_program_check
  1109. }
  1110. }
  1111. }
  1112. fn generate_constraint_mint(
  1113. f: &Field,
  1114. c: &ConstraintTokenMintGroup,
  1115. accs: &AccountsStruct,
  1116. ) -> proc_macro2::TokenStream {
  1117. let name = &f.ident;
  1118. let account_ref = generate_account_ref(f);
  1119. let decimal_check = match &c.decimals {
  1120. Some(decimals) => quote! {
  1121. if #name.decimals != #decimals {
  1122. return Err(anchor_lang::error::ErrorCode::ConstraintMintDecimals.into());
  1123. }
  1124. },
  1125. None => quote! {},
  1126. };
  1127. let mut optional_check_scope = OptionalCheckScope::new_with_field(accs, name);
  1128. let mint_authority_check = match &c.mint_authority {
  1129. Some(mint_authority) => {
  1130. let mint_authority_optional_check = optional_check_scope.generate_check(mint_authority);
  1131. quote! {
  1132. #mint_authority_optional_check
  1133. if #name.mint_authority != anchor_lang::solana_program::program_option::COption::Some(#mint_authority.key()) {
  1134. return Err(anchor_lang::error::ErrorCode::ConstraintMintMintAuthority.into());
  1135. }
  1136. }
  1137. }
  1138. None => quote! {},
  1139. };
  1140. let freeze_authority_check = match &c.freeze_authority {
  1141. Some(freeze_authority) => {
  1142. let freeze_authority_optional_check =
  1143. optional_check_scope.generate_check(freeze_authority);
  1144. quote! {
  1145. #freeze_authority_optional_check
  1146. if #name.freeze_authority != anchor_lang::solana_program::program_option::COption::Some(#freeze_authority.key()) {
  1147. return Err(anchor_lang::error::ErrorCode::ConstraintMintFreezeAuthority.into());
  1148. }
  1149. }
  1150. }
  1151. None => quote! {},
  1152. };
  1153. let token_program_check = match &c.token_program {
  1154. Some(token_program) => {
  1155. let token_program_optional_check = optional_check_scope.generate_check(token_program);
  1156. quote! {
  1157. #token_program_optional_check
  1158. if #account_ref.owner != &#token_program.key() { return Err(anchor_lang::error::ErrorCode::ConstraintMintTokenProgram.into()); }
  1159. }
  1160. }
  1161. None => quote! {},
  1162. };
  1163. let group_pointer_authority_check = match &c.group_pointer_authority {
  1164. Some(group_pointer_authority) => {
  1165. let group_pointer_authority_optional_check =
  1166. optional_check_scope.generate_check(group_pointer_authority);
  1167. quote! {
  1168. let group_pointer = ::anchor_spl::token_interface::get_mint_extension_data::<::anchor_spl::token_interface::spl_token_2022::extension::group_pointer::GroupPointer>(#account_ref);
  1169. if group_pointer.is_err() {
  1170. return Err(anchor_lang::error::ErrorCode::ConstraintMintGroupPointerExtension.into());
  1171. }
  1172. #group_pointer_authority_optional_check
  1173. if group_pointer.unwrap().authority != ::anchor_spl::token_2022_extensions::spl_pod::optional_keys::OptionalNonZeroPubkey::try_from(Some(#group_pointer_authority.key()))? {
  1174. return Err(anchor_lang::error::ErrorCode::ConstraintMintGroupPointerExtensionAuthority.into());
  1175. }
  1176. }
  1177. }
  1178. None => quote! {},
  1179. };
  1180. let group_pointer_group_address_check = match &c.group_pointer_group_address {
  1181. Some(group_pointer_group_address) => {
  1182. let group_pointer_group_address_optional_check =
  1183. optional_check_scope.generate_check(group_pointer_group_address);
  1184. quote! {
  1185. let group_pointer = ::anchor_spl::token_interface::get_mint_extension_data::<::anchor_spl::token_interface::spl_token_2022::extension::group_pointer::GroupPointer>(#account_ref);
  1186. if group_pointer.is_err() {
  1187. return Err(anchor_lang::error::ErrorCode::ConstraintMintGroupPointerExtension.into());
  1188. }
  1189. #group_pointer_group_address_optional_check
  1190. if group_pointer.unwrap().group_address != ::anchor_spl::token_2022_extensions::spl_pod::optional_keys::OptionalNonZeroPubkey::try_from(Some(#group_pointer_group_address.key()))? {
  1191. return Err(anchor_lang::error::ErrorCode::ConstraintMintGroupPointerExtensionGroupAddress.into());
  1192. }
  1193. }
  1194. }
  1195. None => quote! {},
  1196. };
  1197. let group_member_pointer_authority_check = match &c.group_member_pointer_authority {
  1198. Some(group_member_pointer_authority) => {
  1199. let group_member_pointer_authority_optional_check =
  1200. optional_check_scope.generate_check(group_member_pointer_authority);
  1201. quote! {
  1202. let group_member_pointer = ::anchor_spl::token_interface::get_mint_extension_data::<::anchor_spl::token_interface::spl_token_2022::extension::group_member_pointer::GroupMemberPointer>(#account_ref);
  1203. if group_member_pointer.is_err() {
  1204. return Err(anchor_lang::error::ErrorCode::ConstraintMintGroupMemberPointerExtension.into());
  1205. }
  1206. #group_member_pointer_authority_optional_check
  1207. if group_member_pointer.unwrap().authority != ::anchor_spl::token_2022_extensions::spl_pod::optional_keys::OptionalNonZeroPubkey::try_from(Some(#group_member_pointer_authority.key()))? {
  1208. return Err(anchor_lang::error::ErrorCode::ConstraintMintGroupMemberPointerExtensionAuthority.into());
  1209. }
  1210. }
  1211. }
  1212. None => quote! {},
  1213. };
  1214. let group_member_pointer_member_address_check = match &c.group_member_pointer_member_address {
  1215. Some(group_member_pointer_member_address) => {
  1216. let group_member_pointer_member_address_optional_check =
  1217. optional_check_scope.generate_check(group_member_pointer_member_address);
  1218. quote! {
  1219. let group_member_pointer = ::anchor_spl::token_interface::get_mint_extension_data::<::anchor_spl::token_interface::spl_token_2022::extension::group_member_pointer::GroupMemberPointer>(#account_ref);
  1220. if group_member_pointer.is_err() {
  1221. return Err(anchor_lang::error::ErrorCode::ConstraintMintGroupMemberPointerExtension.into());
  1222. }
  1223. #group_member_pointer_member_address_optional_check
  1224. if group_member_pointer.unwrap().member_address != ::anchor_spl::token_2022_extensions::spl_pod::optional_keys::OptionalNonZeroPubkey::try_from(Some(#group_member_pointer_member_address.key()))? {
  1225. return Err(anchor_lang::error::ErrorCode::ConstraintMintGroupMemberPointerExtensionMemberAddress.into());
  1226. }
  1227. }
  1228. }
  1229. None => quote! {},
  1230. };
  1231. let metadata_pointer_authority_check = match &c.metadata_pointer_authority {
  1232. Some(metadata_pointer_authority) => {
  1233. let metadata_pointer_authority_optional_check =
  1234. optional_check_scope.generate_check(metadata_pointer_authority);
  1235. quote! {
  1236. let metadata_pointer = ::anchor_spl::token_interface::get_mint_extension_data::<::anchor_spl::token_interface::spl_token_2022::extension::metadata_pointer::MetadataPointer>(#account_ref);
  1237. if metadata_pointer.is_err() {
  1238. return Err(anchor_lang::error::ErrorCode::ConstraintMintMetadataPointerExtension.into());
  1239. }
  1240. #metadata_pointer_authority_optional_check
  1241. if metadata_pointer.unwrap().authority != ::anchor_spl::token_2022_extensions::spl_pod::optional_keys::OptionalNonZeroPubkey::try_from(Some(#metadata_pointer_authority.key()))? {
  1242. return Err(anchor_lang::error::ErrorCode::ConstraintMintMetadataPointerExtensionAuthority.into());
  1243. }
  1244. }
  1245. }
  1246. None => quote! {},
  1247. };
  1248. let metadata_pointer_metadata_address_check = match &c.metadata_pointer_metadata_address {
  1249. Some(metadata_pointer_metadata_address) => {
  1250. let metadata_pointer_metadata_address_optional_check =
  1251. optional_check_scope.generate_check(metadata_pointer_metadata_address);
  1252. quote! {
  1253. let metadata_pointer = ::anchor_spl::token_interface::get_mint_extension_data::<::anchor_spl::token_interface::spl_token_2022::extension::metadata_pointer::MetadataPointer>(#account_ref);
  1254. if metadata_pointer.is_err() {
  1255. return Err(anchor_lang::error::ErrorCode::ConstraintMintMetadataPointerExtension.into());
  1256. }
  1257. #metadata_pointer_metadata_address_optional_check
  1258. if metadata_pointer.unwrap().metadata_address != ::anchor_spl::token_2022_extensions::spl_pod::optional_keys::OptionalNonZeroPubkey::try_from(Some(#metadata_pointer_metadata_address.key()))? {
  1259. return Err(anchor_lang::error::ErrorCode::ConstraintMintMetadataPointerExtensionMetadataAddress.into());
  1260. }
  1261. }
  1262. }
  1263. None => quote! {},
  1264. };
  1265. let close_authority_check = match &c.close_authority {
  1266. Some(close_authority) => {
  1267. let close_authority_optional_check =
  1268. optional_check_scope.generate_check(close_authority);
  1269. quote! {
  1270. let close_authority = ::anchor_spl::token_interface::get_mint_extension_data::<::anchor_spl::token_interface::spl_token_2022::extension::mint_close_authority::MintCloseAuthority>(#account_ref);
  1271. if close_authority.is_err() {
  1272. return Err(anchor_lang::error::ErrorCode::ConstraintMintCloseAuthorityExtension.into());
  1273. }
  1274. #close_authority_optional_check
  1275. if close_authority.unwrap().close_authority != ::anchor_spl::token_2022_extensions::spl_pod::optional_keys::OptionalNonZeroPubkey::try_from(Some(#close_authority.key()))? {
  1276. return Err(anchor_lang::error::ErrorCode::ConstraintMintCloseAuthorityExtensionAuthority.into());
  1277. }
  1278. }
  1279. }
  1280. None => quote! {},
  1281. };
  1282. let permanent_delegate_check = match &c.permanent_delegate {
  1283. Some(permanent_delegate) => {
  1284. let permanent_delegate_optional_check =
  1285. optional_check_scope.generate_check(permanent_delegate);
  1286. quote! {
  1287. let permanent_delegate = ::anchor_spl::token_interface::get_mint_extension_data::<::anchor_spl::token_interface::spl_token_2022::extension::permanent_delegate::PermanentDelegate>(#account_ref);
  1288. if permanent_delegate.is_err() {
  1289. return Err(anchor_lang::error::ErrorCode::ConstraintMintPermanentDelegateExtension.into());
  1290. }
  1291. #permanent_delegate_optional_check
  1292. if permanent_delegate.unwrap().delegate != ::anchor_spl::token_2022_extensions::spl_pod::optional_keys::OptionalNonZeroPubkey::try_from(Some(#permanent_delegate.key()))? {
  1293. return Err(anchor_lang::error::ErrorCode::ConstraintMintPermanentDelegateExtensionDelegate.into());
  1294. }
  1295. }
  1296. }
  1297. None => quote! {},
  1298. };
  1299. let transfer_hook_authority_check = match &c.transfer_hook_authority {
  1300. Some(transfer_hook_authority) => {
  1301. let transfer_hook_authority_optional_check =
  1302. optional_check_scope.generate_check(transfer_hook_authority);
  1303. quote! {
  1304. let transfer_hook = ::anchor_spl::token_interface::get_mint_extension_data::<::anchor_spl::token_interface::spl_token_2022::extension::transfer_hook::TransferHook>(#account_ref);
  1305. if transfer_hook.is_err() {
  1306. return Err(anchor_lang::error::ErrorCode::ConstraintMintTransferHookExtension.into());
  1307. }
  1308. #transfer_hook_authority_optional_check
  1309. if transfer_hook.unwrap().authority != ::anchor_spl::token_2022_extensions::spl_pod::optional_keys::OptionalNonZeroPubkey::try_from(Some(#transfer_hook_authority.key()))? {
  1310. return Err(anchor_lang::error::ErrorCode::ConstraintMintTransferHookExtensionAuthority.into());
  1311. }
  1312. }
  1313. }
  1314. None => quote! {},
  1315. };
  1316. let transfer_hook_program_id_check = match &c.transfer_hook_program_id {
  1317. Some(transfer_hook_program_id) => {
  1318. let transfer_hook_program_id_optional_check =
  1319. optional_check_scope.generate_check(transfer_hook_program_id);
  1320. quote! {
  1321. let transfer_hook = ::anchor_spl::token_interface::get_mint_extension_data::<::anchor_spl::token_interface::spl_token_2022::extension::transfer_hook::TransferHook>(#account_ref);
  1322. if transfer_hook.is_err() {
  1323. return Err(anchor_lang::error::ErrorCode::ConstraintMintTransferHookExtension.into());
  1324. }
  1325. #transfer_hook_program_id_optional_check
  1326. if transfer_hook.unwrap().program_id != ::anchor_spl::token_2022_extensions::spl_pod::optional_keys::OptionalNonZeroPubkey::try_from(Some(#transfer_hook_program_id.key()))? {
  1327. return Err(anchor_lang::error::ErrorCode::ConstraintMintTransferHookExtensionProgramId.into());
  1328. }
  1329. }
  1330. }
  1331. None => quote! {},
  1332. };
  1333. quote! {
  1334. {
  1335. #decimal_check
  1336. #mint_authority_check
  1337. #freeze_authority_check
  1338. #token_program_check
  1339. #group_pointer_authority_check
  1340. #group_pointer_group_address_check
  1341. #group_member_pointer_authority_check
  1342. #group_member_pointer_member_address_check
  1343. #metadata_pointer_authority_check
  1344. #metadata_pointer_metadata_address_check
  1345. #close_authority_check
  1346. #permanent_delegate_check
  1347. #transfer_hook_authority_check
  1348. #transfer_hook_program_id_check
  1349. }
  1350. }
  1351. }
  1352. #[derive(Clone, Debug)]
  1353. pub struct OptionalCheckScope<'a> {
  1354. seen: HashSet<String>,
  1355. accounts: &'a AccountsStruct,
  1356. }
  1357. impl<'a> OptionalCheckScope<'a> {
  1358. pub fn new(accounts: &'a AccountsStruct) -> Self {
  1359. Self {
  1360. seen: HashSet::new(),
  1361. accounts,
  1362. }
  1363. }
  1364. pub fn new_with_field(accounts: &'a AccountsStruct, field: impl ToString) -> Self {
  1365. let mut check_scope = Self::new(accounts);
  1366. check_scope.seen.insert(field.to_string());
  1367. check_scope
  1368. }
  1369. pub fn generate_check(&mut self, field: impl ToTokens) -> TokenStream {
  1370. let field_name = parser::tts_to_string(&field);
  1371. if self.seen.contains(&field_name) {
  1372. quote! {}
  1373. } else {
  1374. self.seen.insert(field_name.clone());
  1375. if self.accounts.is_field_optional(&field) {
  1376. quote! {
  1377. let #field = if let Some(ref account) = #field {
  1378. account
  1379. } else {
  1380. return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintAccountIsNone).with_account_name(#field_name));
  1381. };
  1382. }
  1383. } else {
  1384. quote! {}
  1385. }
  1386. }
  1387. }
  1388. }
  1389. fn generate_get_token_account_space(mint: &Expr) -> proc_macro2::TokenStream {
  1390. quote! {
  1391. {
  1392. let mint_info = #mint.to_account_info();
  1393. if *mint_info.owner == ::anchor_spl::token_2022::Token2022::id() {
  1394. use ::anchor_spl::token_2022::spl_token_2022::extension::{BaseStateWithExtensions, ExtensionType, StateWithExtensions};
  1395. use ::anchor_spl::token_2022::spl_token_2022::state::{Account, Mint};
  1396. let mint_data = mint_info.try_borrow_data()?;
  1397. let mint_state = StateWithExtensions::<Mint>::unpack(&mint_data)?;
  1398. let mint_extensions = mint_state.get_extension_types()?;
  1399. let required_extensions = ExtensionType::get_required_init_account_extensions(&mint_extensions);
  1400. ExtensionType::try_calculate_account_len::<Account>(&required_extensions)?
  1401. } else {
  1402. ::anchor_spl::token::TokenAccount::LEN
  1403. }
  1404. }
  1405. }
  1406. }
  1407. // Generated code to create an account with with system program with the
  1408. // given `space` amount of data, owned by `owner`.
  1409. //
  1410. // `seeds_with_nonce` should be given for creating PDAs. Otherwise it's an
  1411. // empty stream.
  1412. //
  1413. // This should only be run within scopes where `system_program` is not Optional
  1414. fn generate_create_account(
  1415. field: &Ident,
  1416. space: proc_macro2::TokenStream,
  1417. owner: proc_macro2::TokenStream,
  1418. payer: proc_macro2::TokenStream,
  1419. seeds_with_nonce: proc_macro2::TokenStream,
  1420. ) -> proc_macro2::TokenStream {
  1421. // Field, payer, and system program are already validated to not be an Option at this point
  1422. quote! {
  1423. // If the account being initialized already has lamports, then
  1424. // return them all back to the payer so that the account has
  1425. // zero lamports when the system program's create instruction
  1426. // is eventually called.
  1427. let __current_lamports = #field.lamports();
  1428. if __current_lamports == 0 {
  1429. // Create the token account with right amount of lamports and space, and the correct owner.
  1430. let space = #space;
  1431. let lamports = __anchor_rent.minimum_balance(space);
  1432. let cpi_accounts = anchor_lang::system_program::CreateAccount {
  1433. from: #payer.to_account_info(),
  1434. to: #field.to_account_info()
  1435. };
  1436. let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts);
  1437. anchor_lang::system_program::create_account(cpi_context.with_signer(&[#seeds_with_nonce]), lamports, space as u64, #owner)?;
  1438. } else {
  1439. require_keys_neq!(#payer.key(), #field.key(), anchor_lang::error::ErrorCode::TryingToInitPayerAsProgramAccount);
  1440. // Fund the account for rent exemption.
  1441. let required_lamports = __anchor_rent
  1442. .minimum_balance(#space)
  1443. .max(1)
  1444. .saturating_sub(__current_lamports);
  1445. if required_lamports > 0 {
  1446. let cpi_accounts = anchor_lang::system_program::Transfer {
  1447. from: #payer.to_account_info(),
  1448. to: #field.to_account_info(),
  1449. };
  1450. let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts);
  1451. anchor_lang::system_program::transfer(cpi_context, required_lamports)?;
  1452. }
  1453. // Allocate space.
  1454. let cpi_accounts = anchor_lang::system_program::Allocate {
  1455. account_to_allocate: #field.to_account_info()
  1456. };
  1457. let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts);
  1458. anchor_lang::system_program::allocate(cpi_context.with_signer(&[#seeds_with_nonce]), #space as u64)?;
  1459. // Assign to the spl token program.
  1460. let cpi_accounts = anchor_lang::system_program::Assign {
  1461. account_to_assign: #field.to_account_info()
  1462. };
  1463. let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts);
  1464. anchor_lang::system_program::assign(cpi_context.with_signer(&[#seeds_with_nonce]), #owner)?;
  1465. }
  1466. }
  1467. }
  1468. pub fn generate_constraint_executable(
  1469. f: &Field,
  1470. _c: &ConstraintExecutable,
  1471. ) -> proc_macro2::TokenStream {
  1472. let name_str = f.ident.to_string();
  1473. let account_ref = generate_account_ref(f);
  1474. // because we are only acting on the field, we know it isnt optional at this point
  1475. // as it was unwrapped in `generate_constraint`
  1476. quote! {
  1477. if !#account_ref.executable {
  1478. return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintExecutable).with_account_name(#name_str));
  1479. }
  1480. }
  1481. }
  1482. fn generate_custom_error(
  1483. account_name: &Ident,
  1484. custom_error: &Option<Expr>,
  1485. error: proc_macro2::TokenStream,
  1486. compared_values: &Option<&(proc_macro2::TokenStream, proc_macro2::TokenStream)>,
  1487. ) -> proc_macro2::TokenStream {
  1488. let account_name = account_name.to_string();
  1489. let mut error = match custom_error {
  1490. Some(error) => {
  1491. quote! { anchor_lang::error::Error::from(#error).with_account_name(#account_name) }
  1492. }
  1493. None => {
  1494. quote! { anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::#error).with_account_name(#account_name) }
  1495. }
  1496. };
  1497. let compared_values = match compared_values {
  1498. Some((left, right)) => quote! { .with_pubkeys((#left, #right)) },
  1499. None => quote! {},
  1500. };
  1501. error.extend(compared_values);
  1502. quote! {
  1503. Err(#error)
  1504. }
  1505. }
  1506. fn generate_account_ref(field: &Field) -> proc_macro2::TokenStream {
  1507. let name = &field.ident;
  1508. match &field.ty {
  1509. Ty::AccountInfo => quote!(&#name),
  1510. Ty::Account(acc) if acc.boxed => quote!(AsRef::<AccountInfo>::as_ref(#name.as_ref())),
  1511. Ty::InterfaceAccount(acc) if acc.boxed => {
  1512. quote!(AsRef::<AccountInfo>::as_ref(#name.as_ref()))
  1513. }
  1514. _ => quote!(AsRef::<AccountInfo>::as_ref(&#name)),
  1515. }
  1516. }