create_idempotent.rs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. #![cfg(feature = "test-sbf")]
  2. mod program_test;
  3. use {
  4. program_test::program_test_2022,
  5. solana_program::{instruction::*, pubkey::Pubkey},
  6. solana_program_test::*,
  7. solana_sdk::{
  8. account::Account as SolanaAccount,
  9. program_option::COption,
  10. program_pack::Pack,
  11. signature::Signer,
  12. signer::keypair::Keypair,
  13. system_instruction::create_account,
  14. transaction::{Transaction, TransactionError},
  15. },
  16. spl_associated_token_account::{
  17. error::AssociatedTokenAccountError,
  18. instruction::{
  19. create_associated_token_account, create_associated_token_account_idempotent,
  20. },
  21. },
  22. spl_associated_token_account_client::address::get_associated_token_address_with_program_id,
  23. spl_token_2022::{
  24. extension::ExtensionType,
  25. instruction::initialize_account,
  26. state::{Account, AccountState},
  27. },
  28. };
  29. #[tokio::test]
  30. async fn success_account_exists() {
  31. let wallet_address = Pubkey::new_unique();
  32. let token_mint_address = Pubkey::new_unique();
  33. let associated_token_address = get_associated_token_address_with_program_id(
  34. &wallet_address,
  35. &token_mint_address,
  36. &spl_token_2022::id(),
  37. );
  38. let (mut banks_client, payer, recent_blockhash) =
  39. program_test_2022(token_mint_address).start().await;
  40. let rent = banks_client.get_rent().await.unwrap();
  41. let expected_token_account_len =
  42. ExtensionType::try_calculate_account_len::<Account>(&[ExtensionType::ImmutableOwner])
  43. .unwrap();
  44. let expected_token_account_balance = rent.minimum_balance(expected_token_account_len);
  45. let instruction = create_associated_token_account_idempotent(
  46. &payer.pubkey(),
  47. &wallet_address,
  48. &token_mint_address,
  49. &spl_token_2022::id(),
  50. );
  51. let transaction = Transaction::new_signed_with_payer(
  52. &[instruction],
  53. Some(&payer.pubkey()),
  54. &[&payer],
  55. recent_blockhash,
  56. );
  57. banks_client.process_transaction(transaction).await.unwrap();
  58. // Associated account now exists
  59. let associated_account = banks_client
  60. .get_account(associated_token_address)
  61. .await
  62. .expect("get_account")
  63. .expect("associated_account not none");
  64. assert_eq!(associated_account.data.len(), expected_token_account_len);
  65. assert_eq!(associated_account.owner, spl_token_2022::id());
  66. assert_eq!(associated_account.lamports, expected_token_account_balance);
  67. // Unchecked instruction fails
  68. let instruction = create_associated_token_account(
  69. &payer.pubkey(),
  70. &wallet_address,
  71. &token_mint_address,
  72. &spl_token_2022::id(),
  73. );
  74. let transaction = Transaction::new_signed_with_payer(
  75. &[instruction],
  76. Some(&payer.pubkey()),
  77. &[&payer],
  78. recent_blockhash,
  79. );
  80. assert_eq!(
  81. banks_client
  82. .process_transaction(transaction)
  83. .await
  84. .unwrap_err()
  85. .unwrap(),
  86. TransactionError::InstructionError(0, InstructionError::IllegalOwner)
  87. );
  88. // Get a new blockhash, succeed with create if non existent
  89. let recent_blockhash = banks_client
  90. .get_new_latest_blockhash(&recent_blockhash)
  91. .await
  92. .unwrap();
  93. let instruction = create_associated_token_account_idempotent(
  94. &payer.pubkey(),
  95. &wallet_address,
  96. &token_mint_address,
  97. &spl_token_2022::id(),
  98. );
  99. let transaction = Transaction::new_signed_with_payer(
  100. &[instruction],
  101. Some(&payer.pubkey()),
  102. &[&payer],
  103. recent_blockhash,
  104. );
  105. banks_client.process_transaction(transaction).await.unwrap();
  106. // Associated account is unchanged
  107. let associated_account = banks_client
  108. .get_account(associated_token_address)
  109. .await
  110. .expect("get_account")
  111. .expect("associated_account not none");
  112. assert_eq!(associated_account.data.len(), expected_token_account_len);
  113. assert_eq!(associated_account.owner, spl_token_2022::id());
  114. assert_eq!(associated_account.lamports, expected_token_account_balance);
  115. }
  116. #[tokio::test]
  117. async fn fail_account_exists_with_wrong_owner() {
  118. let wallet_address = Pubkey::new_unique();
  119. let token_mint_address = Pubkey::new_unique();
  120. let associated_token_address = get_associated_token_address_with_program_id(
  121. &wallet_address,
  122. &token_mint_address,
  123. &spl_token_2022::id(),
  124. );
  125. let wrong_owner = Pubkey::new_unique();
  126. let mut associated_token_account =
  127. SolanaAccount::new(1_000_000_000, Account::LEN, &spl_token_2022::id());
  128. let token_account = Account {
  129. mint: token_mint_address,
  130. owner: wrong_owner,
  131. amount: 0,
  132. delegate: COption::None,
  133. state: AccountState::Initialized,
  134. is_native: COption::None,
  135. delegated_amount: 0,
  136. close_authority: COption::None,
  137. };
  138. Account::pack(token_account, &mut associated_token_account.data).unwrap();
  139. let mut pt = program_test_2022(token_mint_address);
  140. pt.add_account(associated_token_address, associated_token_account);
  141. let (banks_client, payer, recent_blockhash) = pt.start().await;
  142. // fail creating token account if non existent
  143. let instruction = create_associated_token_account_idempotent(
  144. &payer.pubkey(),
  145. &wallet_address,
  146. &token_mint_address,
  147. &spl_token_2022::id(),
  148. );
  149. let transaction = Transaction::new_signed_with_payer(
  150. &[instruction],
  151. Some(&payer.pubkey()),
  152. &[&payer],
  153. recent_blockhash,
  154. );
  155. assert_eq!(
  156. banks_client
  157. .process_transaction(transaction)
  158. .await
  159. .unwrap_err()
  160. .unwrap(),
  161. TransactionError::InstructionError(
  162. 0,
  163. InstructionError::Custom(AssociatedTokenAccountError::InvalidOwner as u32)
  164. )
  165. );
  166. }
  167. #[tokio::test]
  168. async fn fail_non_ata() {
  169. let token_mint_address = Pubkey::new_unique();
  170. let (banks_client, payer, recent_blockhash) =
  171. program_test_2022(token_mint_address).start().await;
  172. let rent = banks_client.get_rent().await.unwrap();
  173. let token_account_len =
  174. ExtensionType::try_calculate_account_len::<Account>(&[ExtensionType::ImmutableOwner])
  175. .unwrap();
  176. let token_account_balance = rent.minimum_balance(token_account_len);
  177. let wallet_address = Pubkey::new_unique();
  178. let account = Keypair::new();
  179. let transaction = Transaction::new_signed_with_payer(
  180. &[
  181. create_account(
  182. &payer.pubkey(),
  183. &account.pubkey(),
  184. token_account_balance,
  185. token_account_len as u64,
  186. &spl_token_2022::id(),
  187. ),
  188. initialize_account(
  189. &spl_token_2022::id(),
  190. &account.pubkey(),
  191. &token_mint_address,
  192. &wallet_address,
  193. )
  194. .unwrap(),
  195. ],
  196. Some(&payer.pubkey()),
  197. &[&payer, &account],
  198. recent_blockhash,
  199. );
  200. banks_client.process_transaction(transaction).await.unwrap();
  201. let mut instruction = create_associated_token_account_idempotent(
  202. &payer.pubkey(),
  203. &wallet_address,
  204. &token_mint_address,
  205. &spl_token_2022::id(),
  206. );
  207. instruction.accounts[1] = AccountMeta::new(account.pubkey(), false); // <-- Invalid associated_account_address
  208. let transaction = Transaction::new_signed_with_payer(
  209. &[instruction],
  210. Some(&payer.pubkey()),
  211. &[&payer],
  212. recent_blockhash,
  213. );
  214. assert_eq!(
  215. banks_client
  216. .process_transaction(transaction)
  217. .await
  218. .unwrap_err()
  219. .unwrap(),
  220. TransactionError::InstructionError(0, InstructionError::InvalidSeeds)
  221. );
  222. }