functional.rs 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460
  1. // Mark this test as SBF-only due to current `ProgramTest` limitations when
  2. // CPIing into the system program
  3. #![cfg(feature = "forbid-additional-mints")]
  4. use {
  5. solana_program_test::{processor, tokio, ProgramTest},
  6. solana_sdk::{
  7. account::Account as SolanaAccount,
  8. account_info::AccountInfo,
  9. entrypoint::ProgramResult,
  10. instruction::{AccountMeta, InstructionError},
  11. program_error::ProgramError,
  12. program_option::COption,
  13. pubkey::Pubkey,
  14. signature::Signer,
  15. signer::keypair::Keypair,
  16. sysvar,
  17. transaction::{Transaction, TransactionError},
  18. },
  19. solana_system_interface::instruction as system_instruction,
  20. spl_tlv_account_resolution::{
  21. account::ExtraAccountMeta, error::AccountResolutionError, seeds::Seed,
  22. state::ExtraAccountMetaList,
  23. },
  24. spl_token_2022_interface::{
  25. extension::{
  26. transfer_hook::TransferHookAccount, BaseStateWithExtensionsMut, ExtensionType,
  27. StateWithExtensionsMut,
  28. },
  29. state::{Account, AccountState, Mint},
  30. },
  31. spl_transfer_hook_interface::{
  32. error::TransferHookError,
  33. get_extra_account_metas_address,
  34. instruction::{
  35. execute_with_extra_account_metas, initialize_extra_account_meta_list,
  36. update_extra_account_meta_list,
  37. },
  38. onchain,
  39. },
  40. };
  41. fn setup(program_id: &Pubkey) -> ProgramTest {
  42. let mut program_test = ProgramTest::new(
  43. "spl_transfer_hook_example",
  44. *program_id,
  45. processor!(spl_transfer_hook_example::processor::process),
  46. );
  47. program_test.add_program("spl_token_2022", spl_token_2022_interface::id(), None);
  48. program_test.prefer_bpf(false);
  49. program_test
  50. }
  51. #[allow(clippy::too_many_arguments)]
  52. fn setup_token_accounts(
  53. program_test: &mut ProgramTest,
  54. program_id: &Pubkey,
  55. mint_address: &Pubkey,
  56. mint_authority: &Pubkey,
  57. source: &Pubkey,
  58. destination: &Pubkey,
  59. owner: &Pubkey,
  60. decimals: u8,
  61. transferring: bool,
  62. ) {
  63. // add mint, source, and destination accounts by hand to always force
  64. // the "transferring" flag to true
  65. let mint_size = ExtensionType::try_calculate_account_len::<Mint>(&[]).unwrap();
  66. let mut mint_data = vec![0; mint_size];
  67. let mut state = StateWithExtensionsMut::<Mint>::unpack_uninitialized(&mut mint_data).unwrap();
  68. let token_amount = 1_000_000_000_000;
  69. state.base = Mint {
  70. mint_authority: COption::Some(*mint_authority),
  71. supply: token_amount,
  72. decimals,
  73. is_initialized: true,
  74. freeze_authority: COption::None,
  75. };
  76. state.pack_base();
  77. program_test.add_account(
  78. *mint_address,
  79. SolanaAccount {
  80. lamports: 1_000_000_000,
  81. data: mint_data,
  82. owner: *program_id,
  83. ..SolanaAccount::default()
  84. },
  85. );
  86. let account_size =
  87. ExtensionType::try_calculate_account_len::<Account>(&[ExtensionType::TransferHookAccount])
  88. .unwrap();
  89. let mut account_data = vec![0; account_size];
  90. let mut state =
  91. StateWithExtensionsMut::<Account>::unpack_uninitialized(&mut account_data).unwrap();
  92. let extension = state.init_extension::<TransferHookAccount>(true).unwrap();
  93. extension.transferring = transferring.into();
  94. let token_amount = 1_000_000_000_000;
  95. state.base = Account {
  96. mint: *mint_address,
  97. owner: *owner,
  98. amount: token_amount,
  99. delegate: COption::None,
  100. state: AccountState::Initialized,
  101. is_native: COption::None,
  102. delegated_amount: 0,
  103. close_authority: COption::None,
  104. };
  105. state.pack_base();
  106. state.init_account_type().unwrap();
  107. program_test.add_account(
  108. *source,
  109. SolanaAccount {
  110. lamports: 1_000_000_000,
  111. data: account_data.clone(),
  112. owner: *program_id,
  113. ..SolanaAccount::default()
  114. },
  115. );
  116. program_test.add_account(
  117. *destination,
  118. SolanaAccount {
  119. lamports: 1_000_000_000,
  120. data: account_data,
  121. owner: *program_id,
  122. ..SolanaAccount::default()
  123. },
  124. );
  125. }
  126. #[tokio::test]
  127. async fn success_execute() {
  128. let program_id = Pubkey::new_unique();
  129. let mut program_test = setup(&program_id);
  130. let token_program_id = spl_token_2022_interface::id();
  131. let wallet = Keypair::new();
  132. let mint_address = spl_transfer_hook_example::mint::id();
  133. let mint_authority = Keypair::new();
  134. let mint_authority_pubkey = mint_authority.pubkey();
  135. let source = Pubkey::new_unique();
  136. let destination = Pubkey::new_unique();
  137. let decimals = 2;
  138. let amount = 0u64;
  139. setup_token_accounts(
  140. &mut program_test,
  141. &token_program_id,
  142. &mint_address,
  143. &mint_authority_pubkey,
  144. &source,
  145. &destination,
  146. &wallet.pubkey(),
  147. decimals,
  148. true,
  149. );
  150. let extra_account_metas_address = get_extra_account_metas_address(&mint_address, &program_id);
  151. let writable_pubkey = Pubkey::new_unique();
  152. let init_extra_account_metas = [
  153. ExtraAccountMeta::new_with_pubkey(&sysvar::instructions::id(), false, false).unwrap(),
  154. ExtraAccountMeta::new_with_pubkey(&mint_authority_pubkey, true, false).unwrap(),
  155. ExtraAccountMeta::new_with_seeds(
  156. &[
  157. Seed::Literal {
  158. bytes: b"seed-prefix".to_vec(),
  159. },
  160. Seed::AccountKey { index: 0 },
  161. ],
  162. false,
  163. true,
  164. )
  165. .unwrap(),
  166. ExtraAccountMeta::new_with_seeds(
  167. &[
  168. Seed::InstructionData {
  169. index: 8, // After instruction discriminator
  170. length: 8, // `u64` (amount)
  171. },
  172. Seed::AccountKey { index: 2 },
  173. ],
  174. false,
  175. true,
  176. )
  177. .unwrap(),
  178. ExtraAccountMeta::new_with_pubkey(&writable_pubkey, false, true).unwrap(),
  179. ];
  180. let extra_pda_1 = Pubkey::find_program_address(
  181. &[
  182. b"seed-prefix", // Literal prefix
  183. source.as_ref(), // Account at index 0
  184. ],
  185. &program_id,
  186. )
  187. .0;
  188. let extra_pda_2 = Pubkey::find_program_address(
  189. &[
  190. &amount.to_le_bytes(), // Instruction data bytes 8 to 16
  191. destination.as_ref(), // Account at index 2
  192. ],
  193. &program_id,
  194. )
  195. .0;
  196. let extra_account_metas = [
  197. AccountMeta::new_readonly(sysvar::instructions::id(), false),
  198. AccountMeta::new_readonly(mint_authority_pubkey, true),
  199. AccountMeta::new(extra_pda_1, false),
  200. AccountMeta::new(extra_pda_2, false),
  201. AccountMeta::new(writable_pubkey, false),
  202. ];
  203. let context = program_test.start_with_context().await;
  204. let rent = context.banks_client.get_rent().await.unwrap();
  205. let rent_lamports = rent
  206. .minimum_balance(ExtraAccountMetaList::size_of(init_extra_account_metas.len()).unwrap());
  207. let transaction = Transaction::new_signed_with_payer(
  208. &[
  209. system_instruction::transfer(
  210. &context.payer.pubkey(),
  211. &extra_account_metas_address,
  212. rent_lamports,
  213. ),
  214. initialize_extra_account_meta_list(
  215. &program_id,
  216. &extra_account_metas_address,
  217. &mint_address,
  218. &mint_authority_pubkey,
  219. &init_extra_account_metas,
  220. ),
  221. ],
  222. Some(&context.payer.pubkey()),
  223. &[&context.payer, &mint_authority],
  224. context.last_blockhash,
  225. );
  226. context
  227. .banks_client
  228. .process_transaction(transaction)
  229. .await
  230. .unwrap();
  231. // fail with missing account
  232. {
  233. let transaction = Transaction::new_signed_with_payer(
  234. &[execute_with_extra_account_metas(
  235. &program_id,
  236. &source,
  237. &mint_address,
  238. &destination,
  239. &wallet.pubkey(),
  240. &extra_account_metas_address,
  241. &extra_account_metas[..2],
  242. amount,
  243. )],
  244. Some(&context.payer.pubkey()),
  245. &[&context.payer, &mint_authority],
  246. context.last_blockhash,
  247. );
  248. let error = context
  249. .banks_client
  250. .process_transaction(transaction)
  251. .await
  252. .unwrap_err()
  253. .unwrap();
  254. assert_eq!(
  255. error,
  256. TransactionError::InstructionError(
  257. 0,
  258. InstructionError::Custom(AccountResolutionError::IncorrectAccount as u32),
  259. )
  260. );
  261. }
  262. // fail with wrong account
  263. {
  264. let extra_account_metas = [
  265. AccountMeta::new_readonly(sysvar::instructions::id(), false),
  266. AccountMeta::new_readonly(mint_authority_pubkey, true),
  267. AccountMeta::new(extra_pda_1, false),
  268. AccountMeta::new(extra_pda_2, false),
  269. AccountMeta::new(Pubkey::new_unique(), false),
  270. ];
  271. let transaction = Transaction::new_signed_with_payer(
  272. &[execute_with_extra_account_metas(
  273. &program_id,
  274. &source,
  275. &mint_address,
  276. &destination,
  277. &wallet.pubkey(),
  278. &extra_account_metas_address,
  279. &extra_account_metas,
  280. amount,
  281. )],
  282. Some(&context.payer.pubkey()),
  283. &[&context.payer, &mint_authority],
  284. context.last_blockhash,
  285. );
  286. let error = context
  287. .banks_client
  288. .process_transaction(transaction)
  289. .await
  290. .unwrap_err()
  291. .unwrap();
  292. assert_eq!(
  293. error,
  294. TransactionError::InstructionError(
  295. 0,
  296. InstructionError::Custom(AccountResolutionError::IncorrectAccount as u32),
  297. )
  298. );
  299. }
  300. // fail with wrong PDA
  301. let wrong_pda_2 = Pubkey::find_program_address(
  302. &[
  303. &99u64.to_le_bytes(), // Wrong data
  304. destination.as_ref(),
  305. ],
  306. &program_id,
  307. )
  308. .0;
  309. {
  310. let extra_account_metas = [
  311. AccountMeta::new_readonly(sysvar::instructions::id(), false),
  312. AccountMeta::new_readonly(mint_authority_pubkey, true),
  313. AccountMeta::new(extra_pda_1, false),
  314. AccountMeta::new(wrong_pda_2, false),
  315. AccountMeta::new(writable_pubkey, false),
  316. ];
  317. let transaction = Transaction::new_signed_with_payer(
  318. &[execute_with_extra_account_metas(
  319. &program_id,
  320. &source,
  321. &mint_address,
  322. &destination,
  323. &wallet.pubkey(),
  324. &extra_account_metas_address,
  325. &extra_account_metas,
  326. amount,
  327. )],
  328. Some(&context.payer.pubkey()),
  329. &[&context.payer, &mint_authority],
  330. context.last_blockhash,
  331. );
  332. let error = context
  333. .banks_client
  334. .process_transaction(transaction)
  335. .await
  336. .unwrap_err()
  337. .unwrap();
  338. assert_eq!(
  339. error,
  340. TransactionError::InstructionError(
  341. 0,
  342. InstructionError::Custom(AccountResolutionError::IncorrectAccount as u32),
  343. )
  344. );
  345. }
  346. // fail with not signer
  347. {
  348. let extra_account_metas = [
  349. AccountMeta::new_readonly(sysvar::instructions::id(), false),
  350. AccountMeta::new_readonly(mint_authority_pubkey, false),
  351. AccountMeta::new(extra_pda_1, false),
  352. AccountMeta::new(extra_pda_2, false),
  353. AccountMeta::new(writable_pubkey, false),
  354. ];
  355. let transaction = Transaction::new_signed_with_payer(
  356. &[execute_with_extra_account_metas(
  357. &program_id,
  358. &source,
  359. &mint_address,
  360. &destination,
  361. &wallet.pubkey(),
  362. &extra_account_metas_address,
  363. &extra_account_metas,
  364. amount,
  365. )],
  366. Some(&context.payer.pubkey()),
  367. &[&context.payer],
  368. context.last_blockhash,
  369. );
  370. let error = context
  371. .banks_client
  372. .process_transaction(transaction)
  373. .await
  374. .unwrap_err()
  375. .unwrap();
  376. assert_eq!(
  377. error,
  378. TransactionError::InstructionError(
  379. 0,
  380. InstructionError::Custom(AccountResolutionError::IncorrectAccount as u32),
  381. )
  382. );
  383. }
  384. // success with correct params
  385. {
  386. let transaction = Transaction::new_signed_with_payer(
  387. &[execute_with_extra_account_metas(
  388. &program_id,
  389. &source,
  390. &mint_address,
  391. &destination,
  392. &wallet.pubkey(),
  393. &extra_account_metas_address,
  394. &extra_account_metas,
  395. amount,
  396. )],
  397. Some(&context.payer.pubkey()),
  398. &[&context.payer, &mint_authority],
  399. context.last_blockhash,
  400. );
  401. context
  402. .banks_client
  403. .process_transaction(transaction)
  404. .await
  405. .unwrap();
  406. }
  407. }
  408. #[tokio::test]
  409. async fn fail_incorrect_derivation() {
  410. let program_id = Pubkey::new_unique();
  411. let mut program_test = setup(&program_id);
  412. let token_program_id = spl_token_2022_interface::id();
  413. let wallet = Keypair::new();
  414. let mint_address = spl_transfer_hook_example::mint::id();
  415. let mint_authority = Keypair::new();
  416. let mint_authority_pubkey = mint_authority.pubkey();
  417. let source = Pubkey::new_unique();
  418. let destination = Pubkey::new_unique();
  419. let decimals = 2;
  420. setup_token_accounts(
  421. &mut program_test,
  422. &token_program_id,
  423. &mint_address,
  424. &mint_authority_pubkey,
  425. &source,
  426. &destination,
  427. &wallet.pubkey(),
  428. decimals,
  429. true,
  430. );
  431. // wrong derivation
  432. let extra_account_metas = get_extra_account_metas_address(&program_id, &mint_address);
  433. let context = program_test.start_with_context().await;
  434. let rent = context.banks_client.get_rent().await.unwrap();
  435. let rent_lamports = rent.minimum_balance(ExtraAccountMetaList::size_of(0).unwrap());
  436. let transaction = Transaction::new_signed_with_payer(
  437. &[
  438. system_instruction::transfer(
  439. &context.payer.pubkey(),
  440. &extra_account_metas,
  441. rent_lamports,
  442. ),
  443. initialize_extra_account_meta_list(
  444. &program_id,
  445. &extra_account_metas,
  446. &mint_address,
  447. &mint_authority_pubkey,
  448. &[],
  449. ),
  450. ],
  451. Some(&context.payer.pubkey()),
  452. &[&context.payer, &mint_authority],
  453. context.last_blockhash,
  454. );
  455. let error = context
  456. .banks_client
  457. .process_transaction(transaction)
  458. .await
  459. .unwrap_err()
  460. .unwrap();
  461. assert_eq!(
  462. error,
  463. TransactionError::InstructionError(1, InstructionError::InvalidSeeds)
  464. );
  465. }
  466. #[tokio::test]
  467. async fn fail_incorrect_mint() {
  468. let program_id = Pubkey::new_unique();
  469. let mut program_test = setup(&program_id);
  470. let token_program_id = spl_token_2022_interface::id();
  471. let wallet = Keypair::new();
  472. // wrong mint, only `spl_transfer_hook_example::mint::id()` allowed
  473. let mint_address = Pubkey::new_unique();
  474. let mint_authority = Keypair::new();
  475. let mint_authority_pubkey = mint_authority.pubkey();
  476. let source = Pubkey::new_unique();
  477. let destination = Pubkey::new_unique();
  478. let decimals = 2;
  479. setup_token_accounts(
  480. &mut program_test,
  481. &token_program_id,
  482. &mint_address,
  483. &mint_authority_pubkey,
  484. &source,
  485. &destination,
  486. &wallet.pubkey(),
  487. decimals,
  488. true,
  489. );
  490. let extra_account_metas = get_extra_account_metas_address(&mint_address, &program_id);
  491. let context = program_test.start_with_context().await;
  492. let rent = context.banks_client.get_rent().await.unwrap();
  493. let rent_lamports = rent.minimum_balance(ExtraAccountMetaList::size_of(0).unwrap());
  494. let transaction = Transaction::new_signed_with_payer(
  495. &[
  496. system_instruction::transfer(
  497. &context.payer.pubkey(),
  498. &extra_account_metas,
  499. rent_lamports,
  500. ),
  501. initialize_extra_account_meta_list(
  502. &program_id,
  503. &extra_account_metas,
  504. &mint_address,
  505. &mint_authority_pubkey,
  506. &[],
  507. ),
  508. ],
  509. Some(&context.payer.pubkey()),
  510. &[&context.payer, &mint_authority],
  511. context.last_blockhash,
  512. );
  513. let error = context
  514. .banks_client
  515. .process_transaction(transaction)
  516. .await
  517. .unwrap_err()
  518. .unwrap();
  519. assert_eq!(
  520. error,
  521. TransactionError::InstructionError(1, InstructionError::InvalidArgument)
  522. );
  523. }
  524. /// Test program to CPI into default transfer-hook-interface program
  525. pub fn process_instruction(
  526. _program_id: &Pubkey,
  527. accounts: &[AccountInfo],
  528. input: &[u8],
  529. ) -> ProgramResult {
  530. let amount = input
  531. .get(8..16)
  532. .and_then(|slice| slice.try_into().ok())
  533. .map(u64::from_le_bytes)
  534. .ok_or(ProgramError::InvalidInstructionData)?;
  535. onchain::invoke_execute(
  536. accounts[0].key,
  537. accounts[1].clone(),
  538. accounts[2].clone(),
  539. accounts[3].clone(),
  540. accounts[4].clone(),
  541. &accounts[5..],
  542. amount,
  543. )
  544. }
  545. #[tokio::test]
  546. async fn success_on_chain_invoke() {
  547. let hook_program_id = Pubkey::new_unique();
  548. let mut program_test = setup(&hook_program_id);
  549. let program_id = Pubkey::new_unique();
  550. program_test.add_program(
  551. "test_cpi_program",
  552. program_id,
  553. processor!(process_instruction),
  554. );
  555. let token_program_id = spl_token_2022_interface::id();
  556. let wallet = Keypair::new();
  557. let mint_address = spl_transfer_hook_example::mint::id();
  558. let mint_authority = Keypair::new();
  559. let mint_authority_pubkey = mint_authority.pubkey();
  560. let source = Pubkey::new_unique();
  561. let destination = Pubkey::new_unique();
  562. let decimals = 2;
  563. let amount = 0u64;
  564. setup_token_accounts(
  565. &mut program_test,
  566. &token_program_id,
  567. &mint_address,
  568. &mint_authority_pubkey,
  569. &source,
  570. &destination,
  571. &wallet.pubkey(),
  572. decimals,
  573. true,
  574. );
  575. let extra_account_metas_address =
  576. get_extra_account_metas_address(&mint_address, &hook_program_id);
  577. let writable_pubkey = Pubkey::new_unique();
  578. let init_extra_account_metas = [
  579. ExtraAccountMeta::new_with_pubkey(&sysvar::instructions::id(), false, false).unwrap(),
  580. ExtraAccountMeta::new_with_pubkey(&mint_authority_pubkey, true, false).unwrap(),
  581. ExtraAccountMeta::new_with_seeds(
  582. &[
  583. Seed::Literal {
  584. bytes: b"seed-prefix".to_vec(),
  585. },
  586. Seed::AccountKey { index: 0 },
  587. ],
  588. false,
  589. true,
  590. )
  591. .unwrap(),
  592. ExtraAccountMeta::new_with_seeds(
  593. &[
  594. Seed::InstructionData {
  595. index: 8, // After instruction discriminator
  596. length: 8, // `u64` (amount)
  597. },
  598. Seed::AccountKey { index: 2 },
  599. ],
  600. false,
  601. true,
  602. )
  603. .unwrap(),
  604. ExtraAccountMeta::new_with_pubkey(&writable_pubkey, false, true).unwrap(),
  605. ];
  606. let extra_pda_1 = Pubkey::find_program_address(
  607. &[
  608. b"seed-prefix", // Literal prefix
  609. source.as_ref(), // Account at index 0
  610. ],
  611. &hook_program_id,
  612. )
  613. .0;
  614. let extra_pda_2 = Pubkey::find_program_address(
  615. &[
  616. &amount.to_le_bytes(), // Instruction data bytes 8 to 16
  617. destination.as_ref(), // Account at index 2
  618. ],
  619. &hook_program_id,
  620. )
  621. .0;
  622. let extra_account_metas = [
  623. AccountMeta::new_readonly(sysvar::instructions::id(), false),
  624. AccountMeta::new_readonly(mint_authority_pubkey, true),
  625. AccountMeta::new(extra_pda_1, false),
  626. AccountMeta::new(extra_pda_2, false),
  627. AccountMeta::new(writable_pubkey, false),
  628. ];
  629. let context = program_test.start_with_context().await;
  630. let rent = context.banks_client.get_rent().await.unwrap();
  631. let rent_lamports = rent
  632. .minimum_balance(ExtraAccountMetaList::size_of(init_extra_account_metas.len()).unwrap());
  633. let transaction = Transaction::new_signed_with_payer(
  634. &[
  635. system_instruction::transfer(
  636. &context.payer.pubkey(),
  637. &extra_account_metas_address,
  638. rent_lamports,
  639. ),
  640. initialize_extra_account_meta_list(
  641. &hook_program_id,
  642. &extra_account_metas_address,
  643. &mint_address,
  644. &mint_authority_pubkey,
  645. &init_extra_account_metas,
  646. ),
  647. ],
  648. Some(&context.payer.pubkey()),
  649. &[&context.payer, &mint_authority],
  650. context.last_blockhash,
  651. );
  652. context
  653. .banks_client
  654. .process_transaction(transaction)
  655. .await
  656. .unwrap();
  657. // easier to hack this up!
  658. let mut test_instruction = execute_with_extra_account_metas(
  659. &program_id,
  660. &source,
  661. &mint_address,
  662. &destination,
  663. &wallet.pubkey(),
  664. &extra_account_metas_address,
  665. &extra_account_metas,
  666. amount,
  667. );
  668. test_instruction
  669. .accounts
  670. .insert(0, AccountMeta::new_readonly(hook_program_id, false));
  671. let transaction = Transaction::new_signed_with_payer(
  672. &[test_instruction],
  673. Some(&context.payer.pubkey()),
  674. &[&context.payer, &mint_authority],
  675. context.last_blockhash,
  676. );
  677. context
  678. .banks_client
  679. .process_transaction(transaction)
  680. .await
  681. .unwrap();
  682. }
  683. #[tokio::test]
  684. async fn fail_without_transferring_flag() {
  685. let program_id = Pubkey::new_unique();
  686. let mut program_test = setup(&program_id);
  687. let token_program_id = spl_token_2022_interface::id();
  688. let wallet = Keypair::new();
  689. let mint_address = spl_transfer_hook_example::mint::id();
  690. let mint_authority = Keypair::new();
  691. let mint_authority_pubkey = mint_authority.pubkey();
  692. let source = Pubkey::new_unique();
  693. let destination = Pubkey::new_unique();
  694. let decimals = 2;
  695. setup_token_accounts(
  696. &mut program_test,
  697. &token_program_id,
  698. &mint_address,
  699. &mint_authority_pubkey,
  700. &source,
  701. &destination,
  702. &wallet.pubkey(),
  703. decimals,
  704. false,
  705. );
  706. let extra_account_metas_address = get_extra_account_metas_address(&mint_address, &program_id);
  707. let extra_account_metas = [];
  708. let init_extra_account_metas = [];
  709. let context = program_test.start_with_context().await;
  710. let rent = context.banks_client.get_rent().await.unwrap();
  711. let rent_lamports = rent
  712. .minimum_balance(ExtraAccountMetaList::size_of(init_extra_account_metas.len()).unwrap());
  713. let transaction = Transaction::new_signed_with_payer(
  714. &[
  715. system_instruction::transfer(
  716. &context.payer.pubkey(),
  717. &extra_account_metas_address,
  718. rent_lamports,
  719. ),
  720. initialize_extra_account_meta_list(
  721. &program_id,
  722. &extra_account_metas_address,
  723. &mint_address,
  724. &mint_authority_pubkey,
  725. &init_extra_account_metas,
  726. ),
  727. ],
  728. Some(&context.payer.pubkey()),
  729. &[&context.payer, &mint_authority],
  730. context.last_blockhash,
  731. );
  732. context
  733. .banks_client
  734. .process_transaction(transaction)
  735. .await
  736. .unwrap();
  737. let transaction = Transaction::new_signed_with_payer(
  738. &[execute_with_extra_account_metas(
  739. &program_id,
  740. &source,
  741. &mint_address,
  742. &destination,
  743. &wallet.pubkey(),
  744. &extra_account_metas_address,
  745. &extra_account_metas,
  746. 0,
  747. )],
  748. Some(&context.payer.pubkey()),
  749. &[&context.payer],
  750. context.last_blockhash,
  751. );
  752. let error = context
  753. .banks_client
  754. .process_transaction(transaction)
  755. .await
  756. .unwrap_err()
  757. .unwrap();
  758. assert_eq!(
  759. error,
  760. TransactionError::InstructionError(
  761. 0,
  762. InstructionError::Custom(TransferHookError::ProgramCalledOutsideOfTransfer as u32)
  763. )
  764. );
  765. }
  766. #[tokio::test]
  767. async fn success_on_chain_invoke_with_updated_extra_account_metas() {
  768. let hook_program_id = Pubkey::new_unique();
  769. let mut program_test = setup(&hook_program_id);
  770. let program_id = Pubkey::new_unique();
  771. program_test.add_program(
  772. "test_cpi_program",
  773. program_id,
  774. processor!(process_instruction),
  775. );
  776. let token_program_id = spl_token_2022_interface::id();
  777. let wallet = Keypair::new();
  778. let mint_address = spl_transfer_hook_example::mint::id();
  779. let mint_authority = Keypair::new();
  780. let mint_authority_pubkey = mint_authority.pubkey();
  781. let source = Pubkey::new_unique();
  782. let destination = Pubkey::new_unique();
  783. let decimals = 2;
  784. let amount = 0u64;
  785. setup_token_accounts(
  786. &mut program_test,
  787. &token_program_id,
  788. &mint_address,
  789. &mint_authority_pubkey,
  790. &source,
  791. &destination,
  792. &wallet.pubkey(),
  793. decimals,
  794. true,
  795. );
  796. let extra_account_metas_address =
  797. get_extra_account_metas_address(&mint_address, &hook_program_id);
  798. let writable_pubkey = Pubkey::new_unique();
  799. // Create an initial account metas list
  800. let init_extra_account_metas = [
  801. ExtraAccountMeta::new_with_pubkey(&sysvar::instructions::id(), false, false).unwrap(),
  802. ExtraAccountMeta::new_with_pubkey(&mint_authority_pubkey, true, false).unwrap(),
  803. ExtraAccountMeta::new_with_seeds(
  804. &[
  805. Seed::Literal {
  806. bytes: b"init-seed-prefix".to_vec(),
  807. },
  808. Seed::AccountKey { index: 0 },
  809. ],
  810. false,
  811. true,
  812. )
  813. .unwrap(),
  814. ExtraAccountMeta::new_with_seeds(
  815. &[
  816. Seed::InstructionData {
  817. index: 8, // After instruction discriminator
  818. length: 8, // `u64` (amount)
  819. },
  820. Seed::AccountKey { index: 2 },
  821. ],
  822. false,
  823. true,
  824. )
  825. .unwrap(),
  826. ExtraAccountMeta::new_with_pubkey(&writable_pubkey, false, true).unwrap(),
  827. ];
  828. let context = program_test.start_with_context().await;
  829. let rent = context.banks_client.get_rent().await.unwrap();
  830. let rent_lamports = rent
  831. .minimum_balance(ExtraAccountMetaList::size_of(init_extra_account_metas.len()).unwrap());
  832. let init_transaction = Transaction::new_signed_with_payer(
  833. &[
  834. system_instruction::transfer(
  835. &context.payer.pubkey(),
  836. &extra_account_metas_address,
  837. rent_lamports,
  838. ),
  839. initialize_extra_account_meta_list(
  840. &hook_program_id,
  841. &extra_account_metas_address,
  842. &mint_address,
  843. &mint_authority_pubkey,
  844. &init_extra_account_metas,
  845. ),
  846. ],
  847. Some(&context.payer.pubkey()),
  848. &[&context.payer, &mint_authority],
  849. context.last_blockhash,
  850. );
  851. context
  852. .banks_client
  853. .process_transaction(init_transaction)
  854. .await
  855. .unwrap();
  856. // Create an updated account metas list
  857. let updated_extra_account_metas = [
  858. ExtraAccountMeta::new_with_pubkey(&sysvar::instructions::id(), false, false).unwrap(),
  859. ExtraAccountMeta::new_with_pubkey(&mint_authority_pubkey, true, false).unwrap(),
  860. ExtraAccountMeta::new_with_seeds(
  861. &[
  862. Seed::Literal {
  863. bytes: b"updated-seed-prefix".to_vec(),
  864. },
  865. Seed::AccountKey { index: 0 },
  866. ],
  867. false,
  868. true,
  869. )
  870. .unwrap(),
  871. ExtraAccountMeta::new_with_seeds(
  872. &[
  873. Seed::InstructionData {
  874. index: 8, // After instruction discriminator
  875. length: 8, // `u64` (amount)
  876. },
  877. Seed::AccountKey { index: 2 },
  878. ],
  879. false,
  880. true,
  881. )
  882. .unwrap(),
  883. ExtraAccountMeta::new_with_pubkey(&writable_pubkey, false, true).unwrap(),
  884. ];
  885. let rent = context.banks_client.get_rent().await.unwrap();
  886. let rent_lamports = rent
  887. .minimum_balance(ExtraAccountMetaList::size_of(updated_extra_account_metas.len()).unwrap());
  888. let update_transaction = Transaction::new_signed_with_payer(
  889. &[
  890. system_instruction::transfer(
  891. &context.payer.pubkey(),
  892. &extra_account_metas_address,
  893. rent_lamports,
  894. ),
  895. update_extra_account_meta_list(
  896. &hook_program_id,
  897. &extra_account_metas_address,
  898. &mint_address,
  899. &mint_authority_pubkey,
  900. &updated_extra_account_metas,
  901. ),
  902. ],
  903. Some(&context.payer.pubkey()),
  904. &[&context.payer, &mint_authority],
  905. context.last_blockhash,
  906. );
  907. context
  908. .banks_client
  909. .process_transaction(update_transaction)
  910. .await
  911. .unwrap();
  912. let updated_extra_pda_1 = Pubkey::find_program_address(
  913. &[
  914. b"updated-seed-prefix", // Literal prefix
  915. source.as_ref(), // Account at index 0
  916. ],
  917. &hook_program_id,
  918. )
  919. .0;
  920. let extra_pda_2 = Pubkey::find_program_address(
  921. &[
  922. &amount.to_le_bytes(), // Instruction data bytes 8 to 16
  923. destination.as_ref(), // Account at index 2
  924. ],
  925. &hook_program_id,
  926. )
  927. .0;
  928. let test_updated_extra_account_metas = [
  929. AccountMeta::new_readonly(sysvar::instructions::id(), false),
  930. AccountMeta::new_readonly(mint_authority_pubkey, true),
  931. AccountMeta::new(updated_extra_pda_1, false),
  932. AccountMeta::new(extra_pda_2, false),
  933. AccountMeta::new(writable_pubkey, false),
  934. ];
  935. // Use updated account metas list
  936. let mut test_instruction = execute_with_extra_account_metas(
  937. &program_id,
  938. &source,
  939. &mint_address,
  940. &destination,
  941. &wallet.pubkey(),
  942. &extra_account_metas_address,
  943. &test_updated_extra_account_metas,
  944. amount,
  945. );
  946. test_instruction
  947. .accounts
  948. .insert(0, AccountMeta::new_readonly(hook_program_id, false));
  949. let transaction = Transaction::new_signed_with_payer(
  950. &[test_instruction],
  951. Some(&context.payer.pubkey()),
  952. &[&context.payer, &mint_authority],
  953. context.last_blockhash,
  954. );
  955. context
  956. .banks_client
  957. .process_transaction(transaction)
  958. .await
  959. .unwrap();
  960. }
  961. #[tokio::test]
  962. async fn success_execute_with_updated_extra_account_metas() {
  963. let program_id = Pubkey::new_unique();
  964. let mut program_test = setup(&program_id);
  965. let token_program_id = spl_token_2022_interface::id();
  966. let wallet = Keypair::new();
  967. let mint_address = spl_transfer_hook_example::mint::id();
  968. let mint_authority = Keypair::new();
  969. let mint_authority_pubkey = mint_authority.pubkey();
  970. let source = Pubkey::new_unique();
  971. let destination = Pubkey::new_unique();
  972. let decimals = 2;
  973. let amount = 0u64;
  974. setup_token_accounts(
  975. &mut program_test,
  976. &token_program_id,
  977. &mint_address,
  978. &mint_authority_pubkey,
  979. &source,
  980. &destination,
  981. &wallet.pubkey(),
  982. decimals,
  983. true,
  984. );
  985. let extra_account_metas_address = get_extra_account_metas_address(&mint_address, &program_id);
  986. let writable_pubkey = Pubkey::new_unique();
  987. let init_extra_account_metas = [
  988. ExtraAccountMeta::new_with_pubkey(&sysvar::instructions::id(), false, false).unwrap(),
  989. ExtraAccountMeta::new_with_pubkey(&mint_authority_pubkey, true, false).unwrap(),
  990. ExtraAccountMeta::new_with_seeds(
  991. &[
  992. Seed::Literal {
  993. bytes: b"seed-prefix".to_vec(),
  994. },
  995. Seed::AccountKey { index: 0 },
  996. ],
  997. false,
  998. true,
  999. )
  1000. .unwrap(),
  1001. ExtraAccountMeta::new_with_seeds(
  1002. &[
  1003. Seed::InstructionData {
  1004. index: 8, // After instruction discriminator
  1005. length: 8, // `u64` (amount)
  1006. },
  1007. Seed::AccountKey { index: 2 },
  1008. ],
  1009. false,
  1010. true,
  1011. )
  1012. .unwrap(),
  1013. ExtraAccountMeta::new_with_pubkey(&writable_pubkey, false, true).unwrap(),
  1014. ];
  1015. let extra_pda_1 = Pubkey::find_program_address(
  1016. &[
  1017. b"seed-prefix", // Literal prefix
  1018. source.as_ref(), // Account at index 0
  1019. ],
  1020. &program_id,
  1021. )
  1022. .0;
  1023. let extra_pda_2 = Pubkey::find_program_address(
  1024. &[
  1025. &amount.to_le_bytes(), // Instruction data bytes 8 to 16
  1026. destination.as_ref(), // Account at index 2
  1027. ],
  1028. &program_id,
  1029. )
  1030. .0;
  1031. let init_account_metas = [
  1032. AccountMeta::new_readonly(sysvar::instructions::id(), false),
  1033. AccountMeta::new_readonly(mint_authority_pubkey, true),
  1034. AccountMeta::new(extra_pda_1, false),
  1035. AccountMeta::new(extra_pda_2, false),
  1036. AccountMeta::new(writable_pubkey, false),
  1037. ];
  1038. let context = program_test.start_with_context().await;
  1039. let rent = context.banks_client.get_rent().await.unwrap();
  1040. let rent_lamports = rent
  1041. .minimum_balance(ExtraAccountMetaList::size_of(init_extra_account_metas.len()).unwrap());
  1042. let transaction = Transaction::new_signed_with_payer(
  1043. &[
  1044. system_instruction::transfer(
  1045. &context.payer.pubkey(),
  1046. &extra_account_metas_address,
  1047. rent_lamports,
  1048. ),
  1049. initialize_extra_account_meta_list(
  1050. &program_id,
  1051. &extra_account_metas_address,
  1052. &mint_address,
  1053. &mint_authority_pubkey,
  1054. &init_extra_account_metas,
  1055. ),
  1056. ],
  1057. Some(&context.payer.pubkey()),
  1058. &[&context.payer, &mint_authority],
  1059. context.last_blockhash,
  1060. );
  1061. context
  1062. .banks_client
  1063. .process_transaction(transaction)
  1064. .await
  1065. .unwrap();
  1066. let updated_amount = 1u64;
  1067. let updated_writable_pubkey = Pubkey::new_unique();
  1068. // Create updated extra account metas
  1069. let updated_extra_account_metas = [
  1070. ExtraAccountMeta::new_with_pubkey(&sysvar::instructions::id(), false, false).unwrap(),
  1071. ExtraAccountMeta::new_with_pubkey(&mint_authority_pubkey, true, false).unwrap(),
  1072. ExtraAccountMeta::new_with_seeds(
  1073. &[
  1074. Seed::Literal {
  1075. bytes: b"updated-seed-prefix".to_vec(),
  1076. },
  1077. Seed::AccountKey { index: 0 },
  1078. ],
  1079. false,
  1080. true,
  1081. )
  1082. .unwrap(),
  1083. ExtraAccountMeta::new_with_seeds(
  1084. &[
  1085. Seed::InstructionData {
  1086. index: 8, // After instruction discriminator
  1087. length: 8, // `u64` (amount)
  1088. },
  1089. Seed::AccountKey { index: 2 },
  1090. ],
  1091. false,
  1092. true,
  1093. )
  1094. .unwrap(),
  1095. ExtraAccountMeta::new_with_pubkey(&updated_writable_pubkey, false, true).unwrap(),
  1096. ExtraAccountMeta::new_with_seeds(
  1097. &[
  1098. Seed::Literal {
  1099. bytes: b"new-seed-prefix".to_vec(),
  1100. },
  1101. Seed::AccountKey { index: 0 },
  1102. ],
  1103. false,
  1104. true,
  1105. )
  1106. .unwrap(),
  1107. ];
  1108. let updated_extra_pda_1 = Pubkey::find_program_address(
  1109. &[
  1110. b"updated-seed-prefix", // Literal prefix
  1111. source.as_ref(), // Account at index 0
  1112. ],
  1113. &program_id,
  1114. )
  1115. .0;
  1116. let updated_extra_pda_2 = Pubkey::find_program_address(
  1117. &[
  1118. &updated_amount.to_le_bytes(), // Instruction data bytes 8 to 16
  1119. destination.as_ref(), // Account at index 2
  1120. ],
  1121. &program_id,
  1122. )
  1123. .0;
  1124. // add another PDA
  1125. let new_extra_pda = Pubkey::find_program_address(
  1126. &[
  1127. b"new-seed-prefix", // Literal prefix
  1128. source.as_ref(), // Account at index 0
  1129. ],
  1130. &program_id,
  1131. )
  1132. .0;
  1133. let updated_account_metas = [
  1134. AccountMeta::new_readonly(sysvar::instructions::id(), false),
  1135. AccountMeta::new_readonly(mint_authority_pubkey, true),
  1136. AccountMeta::new(updated_extra_pda_1, false),
  1137. AccountMeta::new(updated_extra_pda_2, false),
  1138. AccountMeta::new(updated_writable_pubkey, false),
  1139. AccountMeta::new(new_extra_pda, false),
  1140. ];
  1141. let update_transaction = Transaction::new_signed_with_payer(
  1142. &[
  1143. system_instruction::transfer(
  1144. &context.payer.pubkey(),
  1145. &extra_account_metas_address,
  1146. rent_lamports,
  1147. ),
  1148. update_extra_account_meta_list(
  1149. &program_id,
  1150. &extra_account_metas_address,
  1151. &mint_address,
  1152. &mint_authority_pubkey,
  1153. &updated_extra_account_metas,
  1154. ),
  1155. ],
  1156. Some(&context.payer.pubkey()),
  1157. &[&context.payer, &mint_authority],
  1158. context.last_blockhash,
  1159. );
  1160. context
  1161. .banks_client
  1162. .process_transaction(update_transaction)
  1163. .await
  1164. .unwrap();
  1165. // fail with initial account metas list
  1166. {
  1167. let transaction = Transaction::new_signed_with_payer(
  1168. &[execute_with_extra_account_metas(
  1169. &program_id,
  1170. &source,
  1171. &mint_address,
  1172. &destination,
  1173. &wallet.pubkey(),
  1174. &extra_account_metas_address,
  1175. &init_account_metas,
  1176. updated_amount,
  1177. )],
  1178. Some(&context.payer.pubkey()),
  1179. &[&context.payer, &mint_authority],
  1180. context.last_blockhash,
  1181. );
  1182. let error = context
  1183. .banks_client
  1184. .process_transaction(transaction)
  1185. .await
  1186. .unwrap_err()
  1187. .unwrap();
  1188. assert_eq!(
  1189. error,
  1190. TransactionError::InstructionError(
  1191. 0,
  1192. InstructionError::Custom(AccountResolutionError::IncorrectAccount as u32),
  1193. )
  1194. );
  1195. }
  1196. // fail with missing account
  1197. {
  1198. let transaction = Transaction::new_signed_with_payer(
  1199. &[execute_with_extra_account_metas(
  1200. &program_id,
  1201. &source,
  1202. &mint_address,
  1203. &destination,
  1204. &wallet.pubkey(),
  1205. &extra_account_metas_address,
  1206. &updated_account_metas[..2],
  1207. updated_amount,
  1208. )],
  1209. Some(&context.payer.pubkey()),
  1210. &[&context.payer, &mint_authority],
  1211. context.last_blockhash,
  1212. );
  1213. let error = context
  1214. .banks_client
  1215. .process_transaction(transaction)
  1216. .await
  1217. .unwrap_err()
  1218. .unwrap();
  1219. assert_eq!(
  1220. error,
  1221. TransactionError::InstructionError(
  1222. 0,
  1223. InstructionError::Custom(AccountResolutionError::IncorrectAccount as u32),
  1224. )
  1225. );
  1226. }
  1227. // fail with wrong account
  1228. {
  1229. let extra_account_metas = [
  1230. AccountMeta::new_readonly(sysvar::instructions::id(), false),
  1231. AccountMeta::new_readonly(mint_authority_pubkey, true),
  1232. AccountMeta::new(updated_extra_pda_1, false),
  1233. AccountMeta::new(updated_extra_pda_2, false),
  1234. AccountMeta::new(Pubkey::new_unique(), false),
  1235. ];
  1236. let transaction = Transaction::new_signed_with_payer(
  1237. &[execute_with_extra_account_metas(
  1238. &program_id,
  1239. &source,
  1240. &mint_address,
  1241. &destination,
  1242. &wallet.pubkey(),
  1243. &extra_account_metas_address,
  1244. &extra_account_metas,
  1245. updated_amount,
  1246. )],
  1247. Some(&context.payer.pubkey()),
  1248. &[&context.payer, &mint_authority],
  1249. context.last_blockhash,
  1250. );
  1251. let error = context
  1252. .banks_client
  1253. .process_transaction(transaction)
  1254. .await
  1255. .unwrap_err()
  1256. .unwrap();
  1257. assert_eq!(
  1258. error,
  1259. TransactionError::InstructionError(
  1260. 0,
  1261. InstructionError::Custom(AccountResolutionError::IncorrectAccount as u32),
  1262. )
  1263. );
  1264. }
  1265. // fail with wrong PDA
  1266. let wrong_pda_2 = Pubkey::find_program_address(
  1267. &[
  1268. &99u64.to_le_bytes(), // Wrong data
  1269. destination.as_ref(),
  1270. ],
  1271. &program_id,
  1272. )
  1273. .0;
  1274. {
  1275. let extra_account_metas = [
  1276. AccountMeta::new_readonly(sysvar::instructions::id(), false),
  1277. AccountMeta::new_readonly(mint_authority_pubkey, true),
  1278. AccountMeta::new(updated_extra_pda_1, false),
  1279. AccountMeta::new(wrong_pda_2, false),
  1280. AccountMeta::new(writable_pubkey, false),
  1281. ];
  1282. let transaction = Transaction::new_signed_with_payer(
  1283. &[execute_with_extra_account_metas(
  1284. &program_id,
  1285. &source,
  1286. &mint_address,
  1287. &destination,
  1288. &wallet.pubkey(),
  1289. &extra_account_metas_address,
  1290. &extra_account_metas,
  1291. updated_amount,
  1292. )],
  1293. Some(&context.payer.pubkey()),
  1294. &[&context.payer, &mint_authority],
  1295. context.last_blockhash,
  1296. );
  1297. let error = context
  1298. .banks_client
  1299. .process_transaction(transaction)
  1300. .await
  1301. .unwrap_err()
  1302. .unwrap();
  1303. assert_eq!(
  1304. error,
  1305. TransactionError::InstructionError(
  1306. 0,
  1307. InstructionError::Custom(AccountResolutionError::IncorrectAccount as u32),
  1308. )
  1309. );
  1310. }
  1311. // fail with not signer
  1312. {
  1313. let extra_account_metas = [
  1314. AccountMeta::new_readonly(sysvar::instructions::id(), false),
  1315. AccountMeta::new_readonly(mint_authority_pubkey, false),
  1316. AccountMeta::new(updated_extra_pda_1, false),
  1317. AccountMeta::new(updated_extra_pda_2, false),
  1318. AccountMeta::new(writable_pubkey, false),
  1319. ];
  1320. let transaction = Transaction::new_signed_with_payer(
  1321. &[execute_with_extra_account_metas(
  1322. &program_id,
  1323. &source,
  1324. &mint_address,
  1325. &destination,
  1326. &wallet.pubkey(),
  1327. &extra_account_metas_address,
  1328. &extra_account_metas,
  1329. updated_amount,
  1330. )],
  1331. Some(&context.payer.pubkey()),
  1332. &[&context.payer],
  1333. context.last_blockhash,
  1334. );
  1335. let error = context
  1336. .banks_client
  1337. .process_transaction(transaction)
  1338. .await
  1339. .unwrap_err()
  1340. .unwrap();
  1341. assert_eq!(
  1342. error,
  1343. TransactionError::InstructionError(
  1344. 0,
  1345. InstructionError::Custom(AccountResolutionError::IncorrectAccount as u32),
  1346. )
  1347. );
  1348. }
  1349. // success with correct params
  1350. {
  1351. let transaction = Transaction::new_signed_with_payer(
  1352. &[execute_with_extra_account_metas(
  1353. &program_id,
  1354. &source,
  1355. &mint_address,
  1356. &destination,
  1357. &wallet.pubkey(),
  1358. &extra_account_metas_address,
  1359. &updated_account_metas,
  1360. updated_amount,
  1361. )],
  1362. Some(&context.payer.pubkey()),
  1363. &[&context.payer, &mint_authority],
  1364. context.last_blockhash,
  1365. );
  1366. context
  1367. .banks_client
  1368. .process_transaction(transaction)
  1369. .await
  1370. .unwrap();
  1371. }
  1372. }