constraints.rs 75 KB

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