constraints.rs 75 KB

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