functional.rs 45 KB

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