constraints.rs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  1. use crate::*;
  2. use proc_macro2_diagnostics::SpanDiagnosticExt;
  3. use quote::quote;
  4. use syn::Expr;
  5. pub fn generate(f: &Field) -> proc_macro2::TokenStream {
  6. let constraints = linearize(&f.constraints);
  7. let rent = constraints
  8. .iter()
  9. .any(|c| matches!(c, Constraint::RentExempt(ConstraintRentExempt::Enforce)))
  10. .then(|| quote! { let __anchor_rent = Rent::get()?; })
  11. .unwrap_or_else(|| quote! {});
  12. let checks: Vec<proc_macro2::TokenStream> = constraints
  13. .iter()
  14. .map(|c| generate_constraint(f, c))
  15. .collect();
  16. quote! {
  17. #rent
  18. #(#checks)*
  19. }
  20. }
  21. pub fn generate_composite(f: &CompositeField) -> proc_macro2::TokenStream {
  22. let checks: Vec<proc_macro2::TokenStream> = linearize(&f.constraints)
  23. .iter()
  24. .filter_map(|c| match c {
  25. Constraint::Raw(_) => Some(c),
  26. Constraint::Literal(_) => Some(c),
  27. _ => panic!("Invariant violation: composite constraints can only be raw or literals"),
  28. })
  29. .map(|c| generate_constraint_composite(f, c))
  30. .collect();
  31. quote! {
  32. #(#checks)*
  33. }
  34. }
  35. // Linearizes the constraint group so that constraints with dependencies
  36. // run after those without.
  37. pub fn linearize(c_group: &ConstraintGroup) -> Vec<Constraint> {
  38. let ConstraintGroup {
  39. init,
  40. zeroed,
  41. mutable,
  42. signer,
  43. has_one,
  44. literal,
  45. raw,
  46. owner,
  47. rent_exempt,
  48. seeds,
  49. executable,
  50. state,
  51. close,
  52. address,
  53. associated_token,
  54. } = c_group.clone();
  55. let mut constraints = Vec::new();
  56. if let Some(c) = zeroed {
  57. constraints.push(Constraint::Zeroed(c));
  58. }
  59. if let Some(c) = init {
  60. constraints.push(Constraint::Init(c));
  61. }
  62. if let Some(c) = seeds {
  63. constraints.push(Constraint::Seeds(c));
  64. }
  65. if let Some(c) = associated_token {
  66. constraints.push(Constraint::AssociatedToken(c));
  67. }
  68. if let Some(c) = mutable {
  69. constraints.push(Constraint::Mut(c));
  70. }
  71. if let Some(c) = signer {
  72. constraints.push(Constraint::Signer(c));
  73. }
  74. constraints.append(&mut has_one.into_iter().map(Constraint::HasOne).collect());
  75. constraints.append(&mut literal.into_iter().map(Constraint::Literal).collect());
  76. constraints.append(&mut raw.into_iter().map(Constraint::Raw).collect());
  77. if let Some(c) = owner {
  78. constraints.push(Constraint::Owner(c));
  79. }
  80. if let Some(c) = rent_exempt {
  81. constraints.push(Constraint::RentExempt(c));
  82. }
  83. if let Some(c) = executable {
  84. constraints.push(Constraint::Executable(c));
  85. }
  86. if let Some(c) = state {
  87. constraints.push(Constraint::State(c));
  88. }
  89. if let Some(c) = close {
  90. constraints.push(Constraint::Close(c));
  91. }
  92. if let Some(c) = address {
  93. constraints.push(Constraint::Address(c));
  94. }
  95. constraints
  96. }
  97. fn generate_constraint(f: &Field, c: &Constraint) -> proc_macro2::TokenStream {
  98. match c {
  99. Constraint::Init(c) => generate_constraint_init(f, c),
  100. Constraint::Zeroed(c) => generate_constraint_zeroed(f, c),
  101. Constraint::Mut(c) => generate_constraint_mut(f, c),
  102. Constraint::HasOne(c) => generate_constraint_has_one(f, c),
  103. Constraint::Signer(c) => generate_constraint_signer(f, c),
  104. Constraint::Literal(c) => generate_constraint_literal(c),
  105. Constraint::Raw(c) => generate_constraint_raw(c),
  106. Constraint::Owner(c) => generate_constraint_owner(f, c),
  107. Constraint::RentExempt(c) => generate_constraint_rent_exempt(f, c),
  108. Constraint::Seeds(c) => generate_constraint_seeds(f, c),
  109. Constraint::Executable(c) => generate_constraint_executable(f, c),
  110. Constraint::State(c) => generate_constraint_state(f, c),
  111. Constraint::Close(c) => generate_constraint_close(f, c),
  112. Constraint::Address(c) => generate_constraint_address(f, c),
  113. Constraint::AssociatedToken(c) => generate_constraint_associated_token(f, c),
  114. }
  115. }
  116. fn generate_constraint_composite(_f: &CompositeField, c: &Constraint) -> proc_macro2::TokenStream {
  117. match c {
  118. Constraint::Raw(c) => generate_constraint_raw(c),
  119. Constraint::Literal(c) => generate_constraint_literal(c),
  120. _ => panic!("Invariant violation"),
  121. }
  122. }
  123. fn generate_constraint_address(f: &Field, c: &ConstraintAddress) -> proc_macro2::TokenStream {
  124. let field = &f.ident;
  125. let addr = &c.address;
  126. let error = generate_custom_error(&c.error, quote! { ConstraintAddress });
  127. quote! {
  128. if #field.to_account_info().key != &#addr {
  129. return Err(#error);
  130. }
  131. }
  132. }
  133. pub fn generate_constraint_init(f: &Field, c: &ConstraintInitGroup) -> proc_macro2::TokenStream {
  134. generate_constraint_init_group(f, c)
  135. }
  136. pub fn generate_constraint_zeroed(f: &Field, _c: &ConstraintZeroed) -> proc_macro2::TokenStream {
  137. let field = &f.ident;
  138. let ty_decl = f.ty_decl();
  139. let from_account_info = f.from_account_info_unchecked(None);
  140. quote! {
  141. let #field: #ty_decl = {
  142. let mut __data: &[u8] = &#field.try_borrow_data()?;
  143. let mut __disc_bytes = [0u8; 8];
  144. __disc_bytes.copy_from_slice(&__data[..8]);
  145. let __discriminator = u64::from_le_bytes(__disc_bytes);
  146. if __discriminator != 0 {
  147. return Err(anchor_lang::__private::ErrorCode::ConstraintZero.into());
  148. }
  149. #from_account_info
  150. };
  151. }
  152. }
  153. pub fn generate_constraint_close(f: &Field, c: &ConstraintClose) -> proc_macro2::TokenStream {
  154. let field = &f.ident;
  155. let target = &c.sol_dest;
  156. quote! {
  157. if #field.to_account_info().key == #target.to_account_info().key {
  158. return Err(anchor_lang::__private::ErrorCode::ConstraintClose.into());
  159. }
  160. }
  161. }
  162. pub fn generate_constraint_mut(f: &Field, c: &ConstraintMut) -> proc_macro2::TokenStream {
  163. let ident = &f.ident;
  164. let error = generate_custom_error(&c.error, quote! { ConstraintMut });
  165. quote! {
  166. if !#ident.to_account_info().is_writable {
  167. return Err(#error);
  168. }
  169. }
  170. }
  171. pub fn generate_constraint_has_one(f: &Field, c: &ConstraintHasOne) -> proc_macro2::TokenStream {
  172. let target = c.join_target.clone();
  173. let ident = &f.ident;
  174. let field = match &f.ty {
  175. Ty::Loader(_) => quote! {#ident.load()?},
  176. Ty::AccountLoader(_) => quote! {#ident.load()?},
  177. _ => quote! {#ident},
  178. };
  179. let error = generate_custom_error(&c.error, quote! { ConstraintHasOne });
  180. quote! {
  181. if &#field.#target != #target.to_account_info().key {
  182. return Err(#error);
  183. }
  184. }
  185. }
  186. pub fn generate_constraint_signer(f: &Field, c: &ConstraintSigner) -> proc_macro2::TokenStream {
  187. let ident = &f.ident;
  188. let info = match f.ty {
  189. Ty::AccountInfo => quote! { #ident },
  190. Ty::ProgramAccount(_) => quote! { #ident.to_account_info() },
  191. Ty::Account(_) => quote! { #ident.to_account_info() },
  192. Ty::Loader(_) => quote! { #ident.to_account_info() },
  193. Ty::AccountLoader(_) => quote! { #ident.to_account_info() },
  194. Ty::CpiAccount(_) => quote! { #ident.to_account_info() },
  195. _ => panic!("Invalid syntax: signer cannot be specified."),
  196. };
  197. let error = generate_custom_error(&c.error, quote! { ConstraintSigner });
  198. quote! {
  199. if !#info.is_signer {
  200. return Err(#error);
  201. }
  202. }
  203. }
  204. pub fn generate_constraint_literal(c: &ConstraintLiteral) -> proc_macro2::TokenStream {
  205. let lit: proc_macro2::TokenStream = {
  206. let lit = &c.lit;
  207. let constraint = lit.value().replace("\"", "");
  208. let message = format!(
  209. "Deprecated. Should be used with constraint: #[account(constraint = {})]",
  210. constraint,
  211. );
  212. lit.span().warning(message).emit_as_item_tokens();
  213. constraint.parse().unwrap()
  214. };
  215. quote! {
  216. if !(#lit) {
  217. return Err(anchor_lang::__private::ErrorCode::Deprecated.into());
  218. }
  219. }
  220. }
  221. pub fn generate_constraint_raw(c: &ConstraintRaw) -> proc_macro2::TokenStream {
  222. let raw = &c.raw;
  223. let error = generate_custom_error(&c.error, quote! { ConstraintRaw });
  224. quote! {
  225. if !(#raw) {
  226. return Err(#error);
  227. }
  228. }
  229. }
  230. pub fn generate_constraint_owner(f: &Field, c: &ConstraintOwner) -> proc_macro2::TokenStream {
  231. let ident = &f.ident;
  232. let owner_address = &c.owner_address;
  233. let error = generate_custom_error(&c.error, quote! { ConstraintOwner });
  234. quote! {
  235. if #ident.to_account_info().owner != &#owner_address {
  236. return Err(#error);
  237. }
  238. }
  239. }
  240. pub fn generate_constraint_rent_exempt(
  241. f: &Field,
  242. c: &ConstraintRentExempt,
  243. ) -> proc_macro2::TokenStream {
  244. let ident = &f.ident;
  245. let info = quote! {
  246. #ident.to_account_info()
  247. };
  248. match c {
  249. ConstraintRentExempt::Skip => quote! {},
  250. ConstraintRentExempt::Enforce => quote! {
  251. if !__anchor_rent.is_exempt(#info.lamports(), #info.try_data_len()?) {
  252. return Err(anchor_lang::__private::ErrorCode::ConstraintRentExempt.into());
  253. }
  254. },
  255. }
  256. }
  257. fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_macro2::TokenStream {
  258. let payer = {
  259. let p = &c.payer;
  260. quote! {
  261. let payer = #p.to_account_info();
  262. }
  263. };
  264. let seeds_with_nonce = match &c.seeds {
  265. None => quote! {},
  266. Some(c) => {
  267. let s = &mut c.seeds.clone();
  268. // If the seeds came with a trailing comma, we need to chop it off
  269. // before we interpolate them below.
  270. if let Some(pair) = s.pop() {
  271. s.push_value(pair.into_value());
  272. }
  273. let maybe_seeds_plus_comma = (!s.is_empty()).then(|| {
  274. quote! { #s, }
  275. });
  276. let inner = match c.bump.as_ref() {
  277. // Bump target not given. Use the canonical bump.
  278. None => {
  279. quote! {
  280. [
  281. #maybe_seeds_plus_comma
  282. &[
  283. Pubkey::find_program_address(
  284. &[#s],
  285. program_id,
  286. ).1
  287. ][..]
  288. ]
  289. }
  290. }
  291. // Bump target given. Use it.
  292. Some(b) => quote! {
  293. [#maybe_seeds_plus_comma &[#b][..]]
  294. },
  295. };
  296. quote! {
  297. &#inner[..]
  298. }
  299. }
  300. };
  301. generate_init(f, c.if_needed, seeds_with_nonce, payer, &c.space, &c.kind)
  302. }
  303. fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2::TokenStream {
  304. let name = &f.ident;
  305. let s = &mut c.seeds.clone();
  306. // If the seeds came with a trailing comma, we need to chop it off
  307. // before we interpolate them below.
  308. if let Some(pair) = s.pop() {
  309. s.push_value(pair.into_value());
  310. }
  311. // If the bump is provided with init *and target*, then force it to be the
  312. // canonical bump.
  313. if c.is_init && c.bump.is_some() {
  314. let b = c.bump.as_ref().unwrap();
  315. quote! {
  316. let (__program_signer, __bump) = anchor_lang::solana_program::pubkey::Pubkey::find_program_address(
  317. &[#s],
  318. program_id,
  319. );
  320. if #name.to_account_info().key != &__program_signer {
  321. return Err(anchor_lang::__private::ErrorCode::ConstraintSeeds.into());
  322. }
  323. if __bump != #b {
  324. return Err(anchor_lang::__private::ErrorCode::ConstraintSeeds.into());
  325. }
  326. }
  327. } else {
  328. let maybe_seeds_plus_comma = (!s.is_empty()).then(|| {
  329. quote! { #s, }
  330. });
  331. let seeds = match c.bump.as_ref() {
  332. // Bump target not given. Find it.
  333. None => {
  334. quote! {
  335. [
  336. #maybe_seeds_plus_comma
  337. &[
  338. Pubkey::find_program_address(
  339. &[#s],
  340. program_id,
  341. ).1
  342. ][..]
  343. ]
  344. }
  345. }
  346. // Bump target given. Use it.
  347. Some(b) => {
  348. quote! {
  349. [#maybe_seeds_plus_comma &[#b][..]]
  350. }
  351. }
  352. };
  353. quote! {
  354. let __program_signer = Pubkey::create_program_address(
  355. &#seeds[..],
  356. program_id,
  357. ).map_err(|_| anchor_lang::__private::ErrorCode::ConstraintSeeds)?;
  358. if #name.to_account_info().key != &__program_signer {
  359. return Err(anchor_lang::__private::ErrorCode::ConstraintSeeds.into());
  360. }
  361. }
  362. }
  363. }
  364. fn generate_constraint_associated_token(
  365. f: &Field,
  366. c: &ConstraintAssociatedToken,
  367. ) -> proc_macro2::TokenStream {
  368. let name = &f.ident;
  369. let wallet_address = &c.wallet;
  370. let spl_token_mint_address = &c.mint;
  371. quote! {
  372. let __associated_token_address = anchor_spl::associated_token::get_associated_token_address(&#wallet_address.key(), &#spl_token_mint_address.key());
  373. if #name.to_account_info().key != &__associated_token_address {
  374. return Err(anchor_lang::__private::ErrorCode::ConstraintAssociated.into());
  375. }
  376. }
  377. }
  378. // `if_needed` is set if account allocation and initialization is optional.
  379. pub fn generate_init(
  380. f: &Field,
  381. if_needed: bool,
  382. seeds_with_nonce: proc_macro2::TokenStream,
  383. payer: proc_macro2::TokenStream,
  384. space: &Option<Expr>,
  385. kind: &InitKind,
  386. ) -> proc_macro2::TokenStream {
  387. let field = &f.ident;
  388. let ty_decl = f.ty_decl();
  389. let from_account_info = f.from_account_info_unchecked(Some(kind));
  390. let if_needed = if if_needed {
  391. quote! {true}
  392. } else {
  393. quote! {false}
  394. };
  395. match kind {
  396. InitKind::Token { owner, mint } => {
  397. let create_account = generate_create_account(
  398. field,
  399. quote! {anchor_spl::token::TokenAccount::LEN},
  400. quote! {token_program.to_account_info().key},
  401. seeds_with_nonce,
  402. );
  403. quote! {
  404. let #field: #ty_decl = {
  405. if !#if_needed || #field.to_account_info().owner == &anchor_lang::solana_program::system_program::ID {
  406. // Define payer variable.
  407. #payer
  408. // Create the account with the system program.
  409. #create_account
  410. // Initialize the token account.
  411. let cpi_program = token_program.to_account_info();
  412. let accounts = anchor_spl::token::InitializeAccount {
  413. account: #field.to_account_info(),
  414. mint: #mint.to_account_info(),
  415. authority: #owner.to_account_info(),
  416. rent: rent.to_account_info(),
  417. };
  418. let cpi_ctx = CpiContext::new(cpi_program, accounts);
  419. anchor_spl::token::initialize_account(cpi_ctx)?;
  420. }
  421. let pa: #ty_decl = #from_account_info;
  422. pa
  423. };
  424. }
  425. }
  426. InitKind::AssociatedToken { owner, mint } => {
  427. quote! {
  428. let #field: #ty_decl = {
  429. if !#if_needed || #field.to_account_info().owner == &anchor_lang::solana_program::system_program::ID {
  430. #payer
  431. let cpi_program = associated_token_program.to_account_info();
  432. let cpi_accounts = anchor_spl::associated_token::Create {
  433. payer: payer.to_account_info(),
  434. associated_token: #field.to_account_info(),
  435. authority: #owner.to_account_info(),
  436. mint: #mint.to_account_info(),
  437. system_program: system_program.to_account_info(),
  438. token_program: token_program.to_account_info(),
  439. rent: rent.to_account_info(),
  440. };
  441. let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
  442. anchor_spl::associated_token::create(cpi_ctx)?;
  443. }
  444. let pa: #ty_decl = #from_account_info;
  445. pa
  446. };
  447. }
  448. }
  449. InitKind::Mint {
  450. owner,
  451. decimals,
  452. freeze_authority,
  453. } => {
  454. let create_account = generate_create_account(
  455. field,
  456. quote! {anchor_spl::token::Mint::LEN},
  457. quote! {token_program.to_account_info().key},
  458. seeds_with_nonce,
  459. );
  460. let freeze_authority = match freeze_authority {
  461. Some(fa) => quote! { Some(&#fa.key()) },
  462. None => quote! { None },
  463. };
  464. quote! {
  465. let #field: #ty_decl = {
  466. if !#if_needed || #field.to_account_info().owner == &anchor_lang::solana_program::system_program::ID {
  467. // Define payer variable.
  468. #payer
  469. // Create the account with the system program.
  470. #create_account
  471. // Initialize the mint account.
  472. let cpi_program = token_program.to_account_info();
  473. let accounts = anchor_spl::token::InitializeMint {
  474. mint: #field.to_account_info(),
  475. rent: rent.to_account_info(),
  476. };
  477. let cpi_ctx = CpiContext::new(cpi_program, accounts);
  478. anchor_spl::token::initialize_mint(cpi_ctx, #decimals, &#owner.to_account_info().key, #freeze_authority)?;
  479. }
  480. let pa: #ty_decl = #from_account_info;
  481. pa
  482. };
  483. }
  484. }
  485. InitKind::Program { owner } => {
  486. let space = match space {
  487. // If no explicit space param was given, serialize the type to bytes
  488. // and take the length (with +8 for the discriminator.)
  489. None => {
  490. let account_ty = f.account_ty();
  491. match matches!(f.ty, Ty::Loader(_) | Ty::AccountLoader(_)) {
  492. false => {
  493. quote! {
  494. let space = 8 + #account_ty::default().try_to_vec().unwrap().len();
  495. }
  496. }
  497. true => {
  498. quote! {
  499. let space = 8 + anchor_lang::__private::bytemuck::bytes_of(&#account_ty::default()).len();
  500. }
  501. }
  502. }
  503. }
  504. // Explicit account size given. Use it.
  505. Some(s) => quote! {
  506. let space = #s;
  507. },
  508. };
  509. // Owner of the account being created. If not specified,
  510. // default to the currently executing program.
  511. let owner = match owner {
  512. None => quote! {
  513. program_id
  514. },
  515. Some(o) => quote! {
  516. &#o
  517. },
  518. };
  519. let create_account =
  520. generate_create_account(field, quote! {space}, owner, seeds_with_nonce);
  521. quote! {
  522. let #field = {
  523. if !#if_needed || #field.to_account_info().owner == &anchor_lang::solana_program::system_program::ID {
  524. #space
  525. #payer
  526. #create_account
  527. }
  528. let pa: #ty_decl = #from_account_info;
  529. pa
  530. };
  531. }
  532. }
  533. }
  534. }
  535. // Generated code to create an account with with system program with the
  536. // given `space` amount of data, owned by `owner`.
  537. //
  538. // `seeds_with_nonce` should be given for creating PDAs. Otherwise it's an
  539. // empty stream.
  540. pub fn generate_create_account(
  541. field: &Ident,
  542. space: proc_macro2::TokenStream,
  543. owner: proc_macro2::TokenStream,
  544. seeds_with_nonce: proc_macro2::TokenStream,
  545. ) -> proc_macro2::TokenStream {
  546. quote! {
  547. // If the account being initialized already has lamports, then
  548. // return them all back to the payer so that the account has
  549. // zero lamports when the system program's create instruction
  550. // is eventually called.
  551. let __current_lamports = #field.to_account_info().lamports();
  552. if __current_lamports == 0 {
  553. // Create the token account with right amount of lamports and space, and the correct owner.
  554. let lamports = __anchor_rent.minimum_balance(#space);
  555. anchor_lang::solana_program::program::invoke_signed(
  556. &anchor_lang::solana_program::system_instruction::create_account(
  557. payer.to_account_info().key,
  558. #field.to_account_info().key,
  559. lamports,
  560. #space as u64,
  561. #owner,
  562. ),
  563. &[
  564. payer.to_account_info(),
  565. #field.to_account_info(),
  566. system_program.to_account_info(),
  567. ],
  568. &[#seeds_with_nonce],
  569. )?;
  570. } else {
  571. // Fund the account for rent exemption.
  572. let required_lamports = __anchor_rent
  573. .minimum_balance(#space)
  574. .max(1)
  575. .saturating_sub(__current_lamports);
  576. if required_lamports > 0 {
  577. anchor_lang::solana_program::program::invoke(
  578. &anchor_lang::solana_program::system_instruction::transfer(
  579. payer.to_account_info().key,
  580. #field.to_account_info().key,
  581. required_lamports,
  582. ),
  583. &[
  584. payer.to_account_info(),
  585. #field.to_account_info(),
  586. system_program.to_account_info(),
  587. ],
  588. )?;
  589. }
  590. // Allocate space.
  591. anchor_lang::solana_program::program::invoke_signed(
  592. &anchor_lang::solana_program::system_instruction::allocate(
  593. #field.to_account_info().key,
  594. #space as u64,
  595. ),
  596. &[
  597. #field.to_account_info(),
  598. system_program.to_account_info(),
  599. ],
  600. &[#seeds_with_nonce],
  601. )?;
  602. // Assign to the spl token program.
  603. anchor_lang::solana_program::program::invoke_signed(
  604. &anchor_lang::solana_program::system_instruction::assign(
  605. #field.to_account_info().key,
  606. #owner,
  607. ),
  608. &[
  609. #field.to_account_info(),
  610. system_program.to_account_info(),
  611. ],
  612. &[#seeds_with_nonce],
  613. )?;
  614. }
  615. }
  616. }
  617. pub fn generate_constraint_executable(
  618. f: &Field,
  619. _c: &ConstraintExecutable,
  620. ) -> proc_macro2::TokenStream {
  621. let name = &f.ident;
  622. quote! {
  623. if !#name.to_account_info().executable {
  624. return Err(anchor_lang::__private::ErrorCode::ConstraintExecutable.into());
  625. }
  626. }
  627. }
  628. pub fn generate_constraint_state(f: &Field, c: &ConstraintState) -> proc_macro2::TokenStream {
  629. let program_target = c.program_target.clone();
  630. let ident = &f.ident;
  631. let account_ty = match &f.ty {
  632. Ty::CpiState(ty) => &ty.account_type_path,
  633. _ => panic!("Invalid state constraint"),
  634. };
  635. quote! {
  636. // Checks the given state account is the canonical state account for
  637. // the target program.
  638. if #ident.to_account_info().key != &anchor_lang::CpiState::<#account_ty>::address(#program_target.to_account_info().key) {
  639. return Err(anchor_lang::__private::ErrorCode::ConstraintState.into());
  640. }
  641. if #ident.to_account_info().owner != #program_target.to_account_info().key {
  642. return Err(anchor_lang::__private::ErrorCode::ConstraintState.into());
  643. }
  644. }
  645. }
  646. fn generate_custom_error(
  647. custom_error: &Option<Expr>,
  648. error: proc_macro2::TokenStream,
  649. ) -> proc_macro2::TokenStream {
  650. match custom_error {
  651. Some(error) => quote! { #error.into() },
  652. None => quote! { anchor_lang::__private::ErrorCode::#error.into() },
  653. }
  654. }