constraints.rs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823
  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.key() != #addr {
  129. return Err(#error);
  130. }
  131. }
  132. }
  133. pub fn generate_constraint_zeroed(f: &Field, _c: &ConstraintZeroed) -> proc_macro2::TokenStream {
  134. let field = &f.ident;
  135. let ty_decl = f.ty_decl();
  136. let account_ty = f.account_ty();
  137. let from_account_info = f.from_account_info(None);
  138. let header_write = quote! {
  139. {
  140. use anchor_lang::Discriminator;
  141. anchor_lang::accounts::header::write_discriminator(&mut __data, &#account_ty::discriminator());
  142. }
  143. };
  144. // Check the *entire* account header is zero.
  145. quote! {
  146. let #field: #ty_decl = {
  147. {
  148. let mut __data: &mut [u8] = &mut #field.try_borrow_mut_data()?;
  149. let mut __header_bytes = [0u8; 8];
  150. __header_bytes.copy_from_slice(&__data[..8]);
  151. let __header = u64::from_le_bytes(__header_bytes);
  152. if __header != 0 {
  153. return Err(anchor_lang::__private::ErrorCode::ConstraintZero.into());
  154. }
  155. #header_write
  156. }
  157. #from_account_info
  158. };
  159. }
  160. }
  161. pub fn generate_constraint_close(f: &Field, c: &ConstraintClose) -> proc_macro2::TokenStream {
  162. let field = &f.ident;
  163. let target = &c.sol_dest;
  164. quote! {
  165. if #field.key() == #target.key() {
  166. return Err(anchor_lang::error::ErrorCode::ConstraintClose.into());
  167. }
  168. }
  169. }
  170. pub fn generate_constraint_mut(f: &Field, c: &ConstraintMut) -> proc_macro2::TokenStream {
  171. let ident = &f.ident;
  172. let error = generate_custom_error(&c.error, quote! { ConstraintMut });
  173. quote! {
  174. if !#ident.to_account_info().is_writable {
  175. return Err(#error);
  176. }
  177. }
  178. }
  179. pub fn generate_constraint_has_one(f: &Field, c: &ConstraintHasOne) -> proc_macro2::TokenStream {
  180. let target = c.join_target.clone();
  181. let ident = &f.ident;
  182. let field = match &f.ty {
  183. Ty::Loader(_) => quote! {#ident.load()?},
  184. Ty::AccountLoader(_) => quote! {#ident.load()?},
  185. _ => quote! {#ident},
  186. };
  187. let error = generate_custom_error(&c.error, quote! { ConstraintHasOne });
  188. quote! {
  189. if #field.#target != #target.key() {
  190. return Err(#error);
  191. }
  192. }
  193. }
  194. pub fn generate_constraint_signer(f: &Field, c: &ConstraintSigner) -> proc_macro2::TokenStream {
  195. let ident = &f.ident;
  196. let info = match f.ty {
  197. Ty::AccountInfo => quote! { #ident },
  198. Ty::ProgramAccount(_) => quote! { #ident.to_account_info() },
  199. Ty::Account(_) => quote! { #ident.to_account_info() },
  200. Ty::Loader(_) => quote! { #ident.to_account_info() },
  201. Ty::AccountLoader(_) => quote! { #ident.to_account_info() },
  202. Ty::CpiAccount(_) => quote! { #ident.to_account_info() },
  203. _ => panic!("Invalid syntax: signer cannot be specified."),
  204. };
  205. let error = generate_custom_error(&c.error, quote! { ConstraintSigner });
  206. quote! {
  207. if !#info.is_signer {
  208. return Err(#error);
  209. }
  210. }
  211. }
  212. pub fn generate_constraint_literal(c: &ConstraintLiteral) -> proc_macro2::TokenStream {
  213. let lit: proc_macro2::TokenStream = {
  214. let lit = &c.lit;
  215. let constraint = lit.value().replace('\"', "");
  216. let message = format!(
  217. "Deprecated. Should be used with constraint: #[account(constraint = {})]",
  218. constraint,
  219. );
  220. lit.span().warning(message).emit_as_item_tokens();
  221. constraint.parse().unwrap()
  222. };
  223. quote! {
  224. if !(#lit) {
  225. return Err(anchor_lang::error::ErrorCode::Deprecated.into());
  226. }
  227. }
  228. }
  229. pub fn generate_constraint_raw(c: &ConstraintRaw) -> proc_macro2::TokenStream {
  230. let raw = &c.raw;
  231. let error = generate_custom_error(&c.error, quote! { ConstraintRaw });
  232. quote! {
  233. if !(#raw) {
  234. return Err(#error);
  235. }
  236. }
  237. }
  238. pub fn generate_constraint_owner(f: &Field, c: &ConstraintOwner) -> proc_macro2::TokenStream {
  239. let ident = &f.ident;
  240. let owner_address = &c.owner_address;
  241. let error = generate_custom_error(&c.error, quote! { ConstraintOwner });
  242. quote! {
  243. if #ident.as_ref().owner != &#owner_address {
  244. return Err(#error);
  245. }
  246. }
  247. }
  248. pub fn generate_constraint_rent_exempt(
  249. f: &Field,
  250. c: &ConstraintRentExempt,
  251. ) -> proc_macro2::TokenStream {
  252. let ident = &f.ident;
  253. let info = quote! {
  254. #ident.to_account_info()
  255. };
  256. match c {
  257. ConstraintRentExempt::Skip => quote! {},
  258. ConstraintRentExempt::Enforce => quote! {
  259. if !__anchor_rent.is_exempt(#info.lamports(), #info.try_data_len()?) {
  260. return Err(anchor_lang::error::ErrorCode::ConstraintRentExempt.into());
  261. }
  262. },
  263. }
  264. }
  265. fn generate_constraint_init(f: &Field, c: &ConstraintInitGroup) -> proc_macro2::TokenStream {
  266. let field = &f.ident;
  267. let ty_decl = f.ty_decl();
  268. let if_needed = if c.if_needed {
  269. quote! {true}
  270. } else {
  271. quote! {false}
  272. };
  273. let space = &c.space;
  274. // Payer for rent exemption.
  275. let payer = {
  276. let p = &c.payer;
  277. quote! {
  278. let payer = #p.to_account_info();
  279. }
  280. };
  281. // Convert from account info to account context wrapper type.
  282. let from_account_info = f.from_account_info(Some(&c.kind));
  283. // PDA bump seeds.
  284. let (find_pda, seeds_with_bump) = match &c.seeds {
  285. None => (quote! {}, quote! {}),
  286. Some(c) => {
  287. let name_str = f.ident.to_string();
  288. let seeds = &mut c.seeds.clone();
  289. // If the seeds came with a trailing comma, we need to chop it off
  290. // before we interpolate them below.
  291. if let Some(pair) = seeds.pop() {
  292. seeds.push_value(pair.into_value());
  293. }
  294. let maybe_seeds_plus_comma = (!seeds.is_empty()).then(|| {
  295. quote! { #seeds, }
  296. });
  297. (
  298. quote! {
  299. let (__pda_address, __bump) = Pubkey::find_program_address(
  300. &[#maybe_seeds_plus_comma],
  301. program_id,
  302. );
  303. __bumps.insert(#name_str.to_string(), __bump);
  304. },
  305. quote! {
  306. &[
  307. #maybe_seeds_plus_comma
  308. &[__bump][..]
  309. ][..]
  310. },
  311. )
  312. }
  313. };
  314. match &c.kind {
  315. InitKind::Token { owner, mint } => {
  316. let create_account = generate_create_account(
  317. field,
  318. quote! {anchor_spl::token::TokenAccount::LEN},
  319. quote! {&token_program.key()},
  320. seeds_with_bump,
  321. );
  322. quote! {
  323. // Define the bump and pda variable.
  324. #find_pda
  325. let #field: #ty_decl = {
  326. if !#if_needed || #field.as_ref().owner == &anchor_lang::solana_program::system_program::ID {
  327. // Define payer variable.
  328. #payer
  329. // Create the account with the system program.
  330. #create_account
  331. // Initialize the token account.
  332. let cpi_program = token_program.to_account_info();
  333. let accounts = anchor_spl::token::InitializeAccount {
  334. account: #field.to_account_info(),
  335. mint: #mint.to_account_info(),
  336. authority: #owner.to_account_info(),
  337. rent: rent.to_account_info(),
  338. };
  339. let cpi_ctx = anchor_lang::context::CpiContext::new(cpi_program, accounts);
  340. anchor_spl::token::initialize_account(cpi_ctx)?;
  341. }
  342. let pa: #ty_decl = #from_account_info;
  343. if #if_needed {
  344. if pa.mint != #mint.key() {
  345. return Err(anchor_lang::error::ErrorCode::ConstraintTokenMint.into());
  346. }
  347. if pa.owner != #owner.key() {
  348. return Err(anchor_lang::error::ErrorCode::ConstraintTokenOwner.into());
  349. }
  350. }
  351. pa
  352. };
  353. }
  354. }
  355. InitKind::AssociatedToken { owner, mint } => {
  356. quote! {
  357. // Define the bump and pda variable.
  358. #find_pda
  359. let #field: #ty_decl = {
  360. if !#if_needed || #field.as_ref().owner == &anchor_lang::solana_program::system_program::ID {
  361. #payer
  362. let cpi_program = associated_token_program.to_account_info();
  363. let cpi_accounts = anchor_spl::associated_token::Create {
  364. payer: payer.to_account_info(),
  365. associated_token: #field.to_account_info(),
  366. authority: #owner.to_account_info(),
  367. mint: #mint.to_account_info(),
  368. system_program: system_program.to_account_info(),
  369. token_program: token_program.to_account_info(),
  370. rent: rent.to_account_info(),
  371. };
  372. let cpi_ctx = anchor_lang::context::CpiContext::new(cpi_program, cpi_accounts);
  373. anchor_spl::associated_token::create(cpi_ctx)?;
  374. }
  375. let pa: #ty_decl = #from_account_info;
  376. if #if_needed {
  377. if pa.mint != #mint.key() {
  378. return Err(anchor_lang::error::ErrorCode::ConstraintTokenMint.into());
  379. }
  380. if pa.owner != #owner.key() {
  381. return Err(anchor_lang::error::ErrorCode::ConstraintTokenOwner.into());
  382. }
  383. if pa.key() != anchor_spl::associated_token::get_associated_token_address(&#owner.key(), &#mint.key()) {
  384. return Err(anchor_lang::error::ErrorCode::AccountNotAssociatedTokenAccount.into());
  385. }
  386. }
  387. pa
  388. };
  389. }
  390. }
  391. InitKind::Mint {
  392. owner,
  393. decimals,
  394. freeze_authority,
  395. } => {
  396. let create_account = generate_create_account(
  397. field,
  398. quote! {anchor_spl::token::Mint::LEN},
  399. quote! {&token_program.key()},
  400. seeds_with_bump,
  401. );
  402. let freeze_authority = match freeze_authority {
  403. Some(fa) => quote! { Option::<&anchor_lang::prelude::Pubkey>::Some(&#fa.key()) },
  404. None => quote! { Option::<&anchor_lang::prelude::Pubkey>::None },
  405. };
  406. quote! {
  407. // Define the bump and pda variable.
  408. #find_pda
  409. let #field: #ty_decl = {
  410. if !#if_needed || #field.as_ref().owner == &anchor_lang::solana_program::system_program::ID {
  411. // Define payer variable.
  412. #payer
  413. // Create the account with the system program.
  414. #create_account
  415. // Initialize the mint account.
  416. let cpi_program = token_program.to_account_info();
  417. let accounts = anchor_spl::token::InitializeMint {
  418. mint: #field.to_account_info(),
  419. rent: rent.to_account_info(),
  420. };
  421. let cpi_ctx = anchor_lang::context::CpiContext::new(cpi_program, accounts);
  422. anchor_spl::token::initialize_mint(cpi_ctx, #decimals, &#owner.key(), #freeze_authority)?;
  423. }
  424. let pa: #ty_decl = #from_account_info;
  425. if #if_needed {
  426. if pa.mint_authority != anchor_lang::solana_program::program_option::COption::Some(#owner.key()) {
  427. return Err(anchor_lang::error::ErrorCode::ConstraintMintMintAuthority.into());
  428. }
  429. if pa.freeze_authority
  430. .as_ref()
  431. .map(|fa| #freeze_authority.as_ref().map(|expected_fa| fa != *expected_fa).unwrap_or(true))
  432. .unwrap_or(#freeze_authority.is_some()) {
  433. return Err(anchor_lang::error::ErrorCode::ConstraintMintFreezeAuthority.into());
  434. }
  435. if pa.decimals != #decimals {
  436. return Err(anchor_lang::error::ErrorCode::ConstraintMintDecimals.into());
  437. }
  438. }
  439. pa
  440. };
  441. }
  442. }
  443. InitKind::Program { owner } => {
  444. // Define the space variable.
  445. let space = match space {
  446. // If no explicit space param was given, serialize the type to bytes
  447. // and take the length (with +8 for the discriminator.)
  448. None => {
  449. let account_ty = f.account_ty();
  450. match matches!(f.ty, Ty::Loader(_) | Ty::AccountLoader(_)) {
  451. false => {
  452. quote! {
  453. let space = 8 + #account_ty::default().try_to_vec().unwrap().len();
  454. }
  455. }
  456. true => {
  457. quote! {
  458. let space = 8 + anchor_lang::__private::bytemuck::bytes_of(&#account_ty::default()).len();
  459. }
  460. }
  461. }
  462. }
  463. // Explicit account size given. Use it.
  464. Some(s) => quote! {
  465. let space = #s;
  466. },
  467. };
  468. // Define the owner of the account being created. If not specified,
  469. // default to the currently executing program.
  470. let owner = match owner {
  471. None => quote! {
  472. program_id
  473. },
  474. Some(o) => quote! {
  475. &#o
  476. },
  477. };
  478. // CPI to the system program to create the account.
  479. let create_account =
  480. generate_create_account(field, quote! {space}, owner.clone(), seeds_with_bump);
  481. // Write the 8 byte header.
  482. let header_write = {
  483. match &f.ty {
  484. Ty::Account(_)
  485. | Ty::ProgramAccount(_)
  486. | Ty::Loader(_)
  487. | Ty::AccountLoader(_) => {
  488. let account_ty = f.account_ty();
  489. quote! {
  490. {
  491. use anchor_lang::Discriminator;
  492. let mut __data = actual_field.try_borrow_mut_data()?;
  493. anchor_lang::accounts::header::write_discriminator(
  494. &mut __data,
  495. &#account_ty::discriminator(),
  496. );
  497. }
  498. }
  499. }
  500. _ => quote! {},
  501. }
  502. };
  503. // Put it all together.
  504. quote! {
  505. // Define the bump variable.
  506. #find_pda
  507. let #field = {
  508. let actual_field = #field.to_account_info();
  509. let actual_owner = actual_field.owner;
  510. // Define the account space variable.
  511. #space
  512. // Create the account. Always do this in the event
  513. // if needed is not specified or the system program is the owner.
  514. if !#if_needed || actual_owner == &anchor_lang::solana_program::system_program::ID {
  515. // Define the payer variable.
  516. #payer
  517. // CPI to the system program to create.
  518. #create_account
  519. }
  520. // Write the account header into the account data before
  521. // deserializing.
  522. #header_write
  523. // Convert from account info to account context wrapper type.
  524. let pa: #ty_decl = #from_account_info;
  525. // Assert the account was created correctly.
  526. if #if_needed {
  527. if space != actual_field.data_len() {
  528. return Err(anchor_lang::error::ErrorCode::ConstraintSpace.into());
  529. }
  530. if actual_owner != #owner {
  531. return Err(anchor_lang::error::ErrorCode::ConstraintOwner.into());
  532. }
  533. {
  534. let required_lamports = __anchor_rent.minimum_balance(space);
  535. if pa.to_account_info().lamports() < required_lamports {
  536. return Err(anchor_lang::error::ErrorCode::ConstraintRentExempt.into());
  537. }
  538. }
  539. }
  540. // Done.
  541. pa
  542. };
  543. }
  544. }
  545. }
  546. }
  547. fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2::TokenStream {
  548. let name = &f.ident;
  549. let name_str = name.to_string();
  550. let s = &mut c.seeds.clone();
  551. let deriving_program_id = c
  552. .program_seed
  553. .clone()
  554. // If they specified a seeds::program to use when deriving the PDA, use it.
  555. .map(|program_id| quote! { #program_id })
  556. // Otherwise fall back to the current program's program_id.
  557. .unwrap_or(quote! { program_id });
  558. // If the seeds came with a trailing comma, we need to chop it off
  559. // before we interpolate them below.
  560. if let Some(pair) = s.pop() {
  561. s.push_value(pair.into_value());
  562. }
  563. // If the bump is provided with init *and target*, then force it to be the
  564. // canonical bump.
  565. //
  566. // Note that for `#[account(init, seeds)]`, find_program_address has already
  567. // been run in the init constraint.
  568. if c.is_init && c.bump.is_some() {
  569. let b = c.bump.as_ref().unwrap();
  570. quote! {
  571. if #name.key() != __pda_address {
  572. return Err(anchor_lang::error::ErrorCode::ConstraintSeeds.into());
  573. }
  574. if __bump != #b {
  575. return Err(anchor_lang::error::ErrorCode::ConstraintSeeds.into());
  576. }
  577. }
  578. }
  579. // Init seeds but no bump. We already used the canonical to create bump so
  580. // just check the address.
  581. //
  582. // Note that for `#[account(init, seeds)]`, find_program_address has already
  583. // been run in the init constraint.
  584. else if c.is_init {
  585. quote! {
  586. if #name.key() != __pda_address {
  587. return Err(anchor_lang::error::ErrorCode::ConstraintSeeds.into());
  588. }
  589. }
  590. }
  591. // No init. So we just check the address.
  592. else {
  593. let maybe_seeds_plus_comma = (!s.is_empty()).then(|| {
  594. quote! { #s, }
  595. });
  596. let define_pda = match c.bump.as_ref() {
  597. // Bump target not given. Find it.
  598. None => quote! {
  599. let (__pda_address, __bump) = Pubkey::find_program_address(
  600. &[#maybe_seeds_plus_comma],
  601. &#deriving_program_id,
  602. );
  603. __bumps.insert(#name_str.to_string(), __bump);
  604. },
  605. // Bump target given. Use it.
  606. Some(b) => quote! {
  607. let __pda_address = Pubkey::create_program_address(
  608. &[#maybe_seeds_plus_comma &[#b][..]],
  609. &#deriving_program_id,
  610. ).map_err(|_| anchor_lang::error::ErrorCode::ConstraintSeeds)?;
  611. },
  612. };
  613. quote! {
  614. // Define the PDA.
  615. #define_pda
  616. // Check it.
  617. if #name.key() != __pda_address {
  618. return Err(anchor_lang::error::ErrorCode::ConstraintSeeds.into());
  619. }
  620. }
  621. }
  622. }
  623. fn generate_constraint_associated_token(
  624. f: &Field,
  625. c: &ConstraintAssociatedToken,
  626. ) -> proc_macro2::TokenStream {
  627. let name = &f.ident;
  628. let wallet_address = &c.wallet;
  629. let spl_token_mint_address = &c.mint;
  630. quote! {
  631. if #name.owner != #wallet_address.key() {
  632. return Err(anchor_lang::error::ErrorCode::ConstraintTokenOwner.into());
  633. }
  634. let __associated_token_address = anchor_spl::associated_token::get_associated_token_address(&#wallet_address.key(), &#spl_token_mint_address.key());
  635. if #name.key() != __associated_token_address {
  636. return Err(anchor_lang::error::ErrorCode::ConstraintAssociated.into());
  637. }
  638. }
  639. }
  640. // Generated code to create an account with with system program with the
  641. // given `space` amount of data, owned by `owner`.
  642. //
  643. // `seeds_with_nonce` should be given for creating PDAs. Otherwise it's an
  644. // empty stream.
  645. pub fn generate_create_account(
  646. field: &Ident,
  647. space: proc_macro2::TokenStream,
  648. owner: proc_macro2::TokenStream,
  649. seeds_with_nonce: proc_macro2::TokenStream,
  650. ) -> proc_macro2::TokenStream {
  651. quote! {
  652. // If the account being initialized already has lamports, then
  653. // return them all back to the payer so that the account has
  654. // zero lamports when the system program's create instruction
  655. // is eventually called.
  656. let __current_lamports = #field.lamports();
  657. if __current_lamports == 0 {
  658. // Create the token account with right amount of lamports and space, and the correct owner.
  659. let lamports = __anchor_rent.minimum_balance(#space);
  660. anchor_lang::solana_program::program::invoke_signed(
  661. &anchor_lang::solana_program::system_instruction::create_account(
  662. &payer.key(),
  663. &#field.key(),
  664. lamports,
  665. #space as u64,
  666. #owner,
  667. ),
  668. &[
  669. payer.to_account_info(),
  670. #field.to_account_info(),
  671. system_program.to_account_info(),
  672. ],
  673. &[#seeds_with_nonce],
  674. )?;
  675. } else {
  676. // Fund the account for rent exemption.
  677. let required_lamports = __anchor_rent
  678. .minimum_balance(#space)
  679. .max(1)
  680. .saturating_sub(__current_lamports);
  681. if required_lamports > 0 {
  682. anchor_lang::solana_program::program::invoke(
  683. &anchor_lang::solana_program::system_instruction::transfer(
  684. &payer.key(),
  685. &#field.key(),
  686. required_lamports,
  687. ),
  688. &[
  689. payer.to_account_info(),
  690. #field.to_account_info(),
  691. system_program.to_account_info(),
  692. ],
  693. )?;
  694. }
  695. // Allocate space.
  696. anchor_lang::solana_program::program::invoke_signed(
  697. &anchor_lang::solana_program::system_instruction::allocate(
  698. &#field.key(),
  699. #space as u64,
  700. ),
  701. &[
  702. #field.to_account_info(),
  703. system_program.to_account_info(),
  704. ],
  705. &[#seeds_with_nonce],
  706. )?;
  707. // Assign to the spl token program.
  708. anchor_lang::solana_program::program::invoke_signed(
  709. &anchor_lang::solana_program::system_instruction::assign(
  710. &#field.key(),
  711. #owner,
  712. ),
  713. &[
  714. #field.to_account_info(),
  715. system_program.to_account_info(),
  716. ],
  717. &[#seeds_with_nonce],
  718. )?;
  719. }
  720. }
  721. }
  722. pub fn generate_constraint_executable(
  723. f: &Field,
  724. _c: &ConstraintExecutable,
  725. ) -> proc_macro2::TokenStream {
  726. let name = &f.ident;
  727. quote! {
  728. if !#name.to_account_info().executable {
  729. return Err(anchor_lang::error::ErrorCode::ConstraintExecutable.into());
  730. }
  731. }
  732. }
  733. pub fn generate_constraint_state(f: &Field, c: &ConstraintState) -> proc_macro2::TokenStream {
  734. let program_target = c.program_target.clone();
  735. let ident = &f.ident;
  736. let account_ty = match &f.ty {
  737. Ty::CpiState(ty) => &ty.account_type_path,
  738. _ => panic!("Invalid state constraint"),
  739. };
  740. quote! {
  741. // Checks the given state account is the canonical state account for
  742. // the target program.
  743. if #ident.key() != anchor_lang::accounts::cpi_state::CpiState::<#account_ty>::address(&#program_target.key()) {
  744. return Err(anchor_lang::error::ErrorCode::ConstraintState.into());
  745. }
  746. if #ident.as_ref().owner != &#program_target.key() {
  747. return Err(anchor_lang::error::ErrorCode::ConstraintState.into());
  748. }
  749. }
  750. }
  751. fn generate_custom_error(
  752. custom_error: &Option<Expr>,
  753. error: proc_macro2::TokenStream,
  754. ) -> proc_macro2::TokenStream {
  755. match custom_error {
  756. Some(error) => quote! { #error.into() },
  757. None => quote! { anchor_lang::error::ErrorCode::#error.into() },
  758. }
  759. }