withdraw_excess_lamports.rs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760
  1. #![allow(clippy::arithmetic_side_effects)]
  2. mod setup;
  3. use {
  4. assert_matches::assert_matches,
  5. setup::{mint, TOKEN_PROGRAM_ID},
  6. solana_program_test::{tokio, BanksClientError, ProgramTest},
  7. solana_sdk::{
  8. instruction::InstructionError,
  9. pubkey::Pubkey,
  10. signature::{Keypair, Signer},
  11. system_instruction,
  12. transaction::{Transaction, TransactionError},
  13. },
  14. spl_token_interface::state::{account::Account, mint::Mint, multisig::Multisig},
  15. std::mem::size_of,
  16. };
  17. #[test_case::test_case(TOKEN_PROGRAM_ID ; "p-token")]
  18. #[tokio::test]
  19. async fn withdraw_excess_lamports_from_mint(token_program: Pubkey) {
  20. let context = ProgramTest::new("pinocchio_token_program", TOKEN_PROGRAM_ID, None)
  21. .start_with_context()
  22. .await;
  23. let excess_lamports = 4_000_000_000_000;
  24. // Given a mint authority, freeze authority and an account keypair.
  25. let mint_authority = Keypair::new();
  26. let freeze_authority = Pubkey::new_unique();
  27. let account = Keypair::new();
  28. let account_pubkey = account.pubkey();
  29. let account_size = size_of::<Mint>();
  30. let rent = context.banks_client.get_rent().await.unwrap();
  31. let mut initialize_ix = spl_token::instruction::initialize_mint(
  32. &spl_token::ID,
  33. &account.pubkey(),
  34. &mint_authority.pubkey(),
  35. Some(&freeze_authority),
  36. 0,
  37. )
  38. .unwrap();
  39. // Switches the program id to the token program.
  40. initialize_ix.program_id = token_program;
  41. // And we initialize a mint account with excess lamports.
  42. let instructions = vec![
  43. system_instruction::create_account(
  44. &context.payer.pubkey(),
  45. &account.pubkey(),
  46. rent.minimum_balance(account_size) + excess_lamports,
  47. account_size as u64,
  48. &token_program,
  49. ),
  50. initialize_ix,
  51. ];
  52. let tx = Transaction::new_signed_with_payer(
  53. &instructions,
  54. Some(&context.payer.pubkey()),
  55. &[&context.payer, &account],
  56. context.last_blockhash,
  57. );
  58. context.banks_client.process_transaction(tx).await.unwrap();
  59. let account = context
  60. .banks_client
  61. .get_account(account.pubkey())
  62. .await
  63. .unwrap();
  64. assert!(account.is_some());
  65. let account = account.unwrap();
  66. assert_eq!(
  67. account.lamports,
  68. rent.minimum_balance(account_size) + excess_lamports
  69. );
  70. // When we withdraw the excess lamports.
  71. let destination = Pubkey::new_unique();
  72. let mut withdraw_ix = spl_token_2022::instruction::withdraw_excess_lamports(
  73. &spl_token_2022::ID,
  74. &account_pubkey,
  75. &destination,
  76. &mint_authority.pubkey(),
  77. &[],
  78. )
  79. .unwrap();
  80. // Switches the program id to the token program.
  81. withdraw_ix.program_id = token_program;
  82. let tx = Transaction::new_signed_with_payer(
  83. &[withdraw_ix],
  84. Some(&context.payer.pubkey()),
  85. &[&context.payer, &mint_authority],
  86. context.last_blockhash,
  87. );
  88. context.banks_client.process_transaction(tx).await.unwrap();
  89. // Then the destination account has the excess lamports.
  90. let destination = context.banks_client.get_account(destination).await.unwrap();
  91. assert!(destination.is_some());
  92. let destination = destination.unwrap();
  93. assert_eq!(destination.lamports, excess_lamports);
  94. }
  95. #[test_case::test_case(TOKEN_PROGRAM_ID ; "p-token")]
  96. #[tokio::test]
  97. async fn withdraw_excess_lamports_from_account(token_program: Pubkey) {
  98. let mut context = ProgramTest::new("pinocchio_token_program", TOKEN_PROGRAM_ID, None)
  99. .start_with_context()
  100. .await;
  101. let excess_lamports = 4_000_000_000_000;
  102. // Given a mint account.
  103. let mint_authority = Pubkey::new_unique();
  104. let freeze_authority = Pubkey::new_unique();
  105. let mint = mint::initialize(
  106. &mut context,
  107. mint_authority,
  108. Some(freeze_authority),
  109. &token_program,
  110. )
  111. .await
  112. .unwrap();
  113. // Given a mint authority, freeze authority and an account keypair.
  114. let owner = Keypair::new();
  115. let account = Keypair::new();
  116. let account_pubkey = account.pubkey();
  117. let account_size = size_of::<Account>();
  118. let rent = context.banks_client.get_rent().await.unwrap();
  119. let mut initialize_ix = spl_token::instruction::initialize_account(
  120. &spl_token::ID,
  121. &account.pubkey(),
  122. &mint,
  123. &owner.pubkey(),
  124. )
  125. .unwrap();
  126. // Switches the program id to the token program.
  127. initialize_ix.program_id = token_program;
  128. // When a new mint account is created and initialized.
  129. let instructions = vec![
  130. system_instruction::create_account(
  131. &context.payer.pubkey(),
  132. &account.pubkey(),
  133. rent.minimum_balance(account_size) + excess_lamports,
  134. account_size as u64,
  135. &token_program,
  136. ),
  137. initialize_ix,
  138. ];
  139. let tx = Transaction::new_signed_with_payer(
  140. &instructions,
  141. Some(&context.payer.pubkey()),
  142. &[&context.payer, &account],
  143. context.last_blockhash,
  144. );
  145. context.banks_client.process_transaction(tx).await.unwrap();
  146. let account = context
  147. .banks_client
  148. .get_account(account.pubkey())
  149. .await
  150. .unwrap();
  151. assert!(account.is_some());
  152. let account = account.unwrap();
  153. assert_eq!(
  154. account.lamports,
  155. rent.minimum_balance(account_size) + excess_lamports
  156. );
  157. // When we withdraw the excess lamports.
  158. let destination = Pubkey::new_unique();
  159. let mut withdraw_ix = spl_token_2022::instruction::withdraw_excess_lamports(
  160. &spl_token_2022::ID,
  161. &account_pubkey,
  162. &destination,
  163. &owner.pubkey(),
  164. &[],
  165. )
  166. .unwrap();
  167. // Switches the program id to the token program.
  168. withdraw_ix.program_id = token_program;
  169. let tx = Transaction::new_signed_with_payer(
  170. &[withdraw_ix],
  171. Some(&context.payer.pubkey()),
  172. &[&context.payer, &owner],
  173. context.last_blockhash,
  174. );
  175. context.banks_client.process_transaction(tx).await.unwrap();
  176. // Then the destination account has the excess lamports.
  177. let destination = context.banks_client.get_account(destination).await.unwrap();
  178. assert!(destination.is_some());
  179. let destination = destination.unwrap();
  180. assert_eq!(destination.lamports, excess_lamports);
  181. }
  182. #[test_case::test_case(TOKEN_PROGRAM_ID ; "p-token")]
  183. #[tokio::test]
  184. async fn withdraw_excess_lamports_from_multisig(token_program: Pubkey) {
  185. let context = ProgramTest::new("pinocchio_token_program", TOKEN_PROGRAM_ID, None)
  186. .start_with_context()
  187. .await;
  188. let excess_lamports = 4_000_000_000_000;
  189. // Given an account
  190. let multisig = Keypair::new();
  191. let signer1 = Keypair::new();
  192. let signer1_pubkey = signer1.pubkey();
  193. let signer2 = Keypair::new();
  194. let signer2_pubkey = signer2.pubkey();
  195. let signer3 = Keypair::new();
  196. let signer3_pubkey = signer3.pubkey();
  197. let signers = vec![&signer1_pubkey, &signer2_pubkey, &signer3_pubkey];
  198. let rent = context.banks_client.get_rent().await.unwrap();
  199. let account_size = size_of::<Multisig>();
  200. let mut initialize_ix = spl_token::instruction::initialize_multisig(
  201. &spl_token::ID,
  202. &multisig.pubkey(),
  203. &signers,
  204. 3,
  205. )
  206. .unwrap();
  207. // Switches the program id to the token program.
  208. initialize_ix.program_id = token_program;
  209. // And we initialize the multisig account.
  210. let instructions = vec![
  211. system_instruction::create_account(
  212. &context.payer.pubkey(),
  213. &multisig.pubkey(),
  214. rent.minimum_balance(account_size) + excess_lamports,
  215. account_size as u64,
  216. &token_program,
  217. ),
  218. initialize_ix,
  219. ];
  220. let tx = Transaction::new_signed_with_payer(
  221. &instructions,
  222. Some(&context.payer.pubkey()),
  223. &[&context.payer, &multisig],
  224. context.last_blockhash,
  225. );
  226. context.banks_client.process_transaction(tx).await.unwrap();
  227. let account = context
  228. .banks_client
  229. .get_account(multisig.pubkey())
  230. .await
  231. .unwrap();
  232. assert!(account.is_some());
  233. let account = account.unwrap();
  234. assert_eq!(
  235. account.lamports,
  236. rent.minimum_balance(account_size) + excess_lamports
  237. );
  238. // When we withdraw the excess lamports.
  239. let destination = Pubkey::new_unique();
  240. let mut withdraw_ix = spl_token_2022::instruction::withdraw_excess_lamports(
  241. &spl_token_2022::ID,
  242. &multisig.pubkey(),
  243. &destination,
  244. &multisig.pubkey(),
  245. &signers,
  246. )
  247. .unwrap();
  248. // Switches the program id to the token program.
  249. withdraw_ix.program_id = token_program;
  250. let tx = Transaction::new_signed_with_payer(
  251. &[withdraw_ix],
  252. Some(&context.payer.pubkey()),
  253. &[&context.payer, &signer1, &signer2, &signer3],
  254. context.last_blockhash,
  255. );
  256. context.banks_client.process_transaction(tx).await.unwrap();
  257. // Then the destination account has the excess lamports.
  258. let destination = context.banks_client.get_account(destination).await.unwrap();
  259. assert!(destination.is_some());
  260. let destination = destination.unwrap();
  261. assert_eq!(destination.lamports, excess_lamports);
  262. }
  263. #[test_case::test_case(TOKEN_PROGRAM_ID ; "p-token")]
  264. #[tokio::test]
  265. async fn fail_withdraw_excess_lamports_from_mint_wrong_authority(token_program: Pubkey) {
  266. let context = ProgramTest::new("pinocchio_token_program", TOKEN_PROGRAM_ID, None)
  267. .start_with_context()
  268. .await;
  269. let excess_lamports = 4_000_000_000_000;
  270. // Given a mint authority, freeze authority and an account keypair.
  271. let mint_authority = Keypair::new();
  272. let freeze_authority = Pubkey::new_unique();
  273. let account = Keypair::new();
  274. let account_pubkey = account.pubkey();
  275. let account_size = size_of::<Mint>();
  276. let rent = context.banks_client.get_rent().await.unwrap();
  277. let mut initialize_ix = spl_token::instruction::initialize_mint(
  278. &spl_token::ID,
  279. &account.pubkey(),
  280. &mint_authority.pubkey(),
  281. Some(&freeze_authority),
  282. 0,
  283. )
  284. .unwrap();
  285. // Switches the program id to the token program.
  286. initialize_ix.program_id = token_program;
  287. // And we initialize a mint account with excess lamports.
  288. let instructions = vec![
  289. system_instruction::create_account(
  290. &context.payer.pubkey(),
  291. &account.pubkey(),
  292. rent.minimum_balance(account_size) + excess_lamports,
  293. account_size as u64,
  294. &token_program,
  295. ),
  296. initialize_ix,
  297. ];
  298. let tx = Transaction::new_signed_with_payer(
  299. &instructions,
  300. Some(&context.payer.pubkey()),
  301. &[&context.payer, &account],
  302. context.last_blockhash,
  303. );
  304. context.banks_client.process_transaction(tx).await.unwrap();
  305. let account = context
  306. .banks_client
  307. .get_account(account.pubkey())
  308. .await
  309. .unwrap();
  310. assert!(account.is_some());
  311. let account = account.unwrap();
  312. assert_eq!(
  313. account.lamports,
  314. rent.minimum_balance(account_size) + excess_lamports
  315. );
  316. // When we try to withdraw the excess lamports with the wrong authority.
  317. let destination = Pubkey::new_unique();
  318. let wrong_authority = Keypair::new();
  319. let mut withdraw_ix = spl_token_2022::instruction::withdraw_excess_lamports(
  320. &spl_token_2022::ID,
  321. &account_pubkey,
  322. &destination,
  323. &wrong_authority.pubkey(),
  324. &[],
  325. )
  326. .unwrap();
  327. // Switches the program id to the token program.
  328. withdraw_ix.program_id = token_program;
  329. let tx = Transaction::new_signed_with_payer(
  330. &[withdraw_ix],
  331. Some(&context.payer.pubkey()),
  332. &[&context.payer, &wrong_authority],
  333. context.last_blockhash,
  334. );
  335. let error = context
  336. .banks_client
  337. .process_transaction(tx)
  338. .await
  339. .unwrap_err();
  340. // The we expect an error.
  341. assert_matches!(
  342. error,
  343. BanksClientError::TransactionError(TransactionError::InstructionError(
  344. _,
  345. InstructionError::Custom(4) // TokenError::OwnerMismatch
  346. ))
  347. );
  348. }
  349. #[test_case::test_case(TOKEN_PROGRAM_ID ; "p-token")]
  350. #[tokio::test]
  351. async fn fail_withdraw_excess_lamports_from_account_wrong_authority(token_program: Pubkey) {
  352. let mut context = ProgramTest::new("pinocchio_token_program", TOKEN_PROGRAM_ID, None)
  353. .start_with_context()
  354. .await;
  355. let excess_lamports = 4_000_000_000_000;
  356. // Given a mint account.
  357. let mint_authority = Pubkey::new_unique();
  358. let freeze_authority = Pubkey::new_unique();
  359. let mint = mint::initialize(
  360. &mut context,
  361. mint_authority,
  362. Some(freeze_authority),
  363. &token_program,
  364. )
  365. .await
  366. .unwrap();
  367. // Given a mint authority, freeze authority and an account keypair.
  368. let owner = Keypair::new();
  369. let account = Keypair::new();
  370. let account_pubkey = account.pubkey();
  371. let account_size = size_of::<Account>();
  372. let rent = context.banks_client.get_rent().await.unwrap();
  373. let mut initialize_ix = spl_token::instruction::initialize_account(
  374. &spl_token::ID,
  375. &account.pubkey(),
  376. &mint,
  377. &owner.pubkey(),
  378. )
  379. .unwrap();
  380. // Switches the program id to the token program.
  381. initialize_ix.program_id = token_program;
  382. // When a new mint account is created and initialized.
  383. let instructions = vec![
  384. system_instruction::create_account(
  385. &context.payer.pubkey(),
  386. &account.pubkey(),
  387. rent.minimum_balance(account_size) + excess_lamports,
  388. account_size as u64,
  389. &token_program,
  390. ),
  391. initialize_ix,
  392. ];
  393. let tx = Transaction::new_signed_with_payer(
  394. &instructions,
  395. Some(&context.payer.pubkey()),
  396. &[&context.payer, &account],
  397. context.last_blockhash,
  398. );
  399. context.banks_client.process_transaction(tx).await.unwrap();
  400. let account = context
  401. .banks_client
  402. .get_account(account.pubkey())
  403. .await
  404. .unwrap();
  405. assert!(account.is_some());
  406. let account = account.unwrap();
  407. assert_eq!(
  408. account.lamports,
  409. rent.minimum_balance(account_size) + excess_lamports
  410. );
  411. // When we try to withdraw the excess lamports with the wrong owner.
  412. let destination = Pubkey::new_unique();
  413. let wrong_owner = Keypair::new();
  414. let mut withdraw_ix = spl_token_2022::instruction::withdraw_excess_lamports(
  415. &spl_token_2022::ID,
  416. &account_pubkey,
  417. &destination,
  418. &wrong_owner.pubkey(),
  419. &[],
  420. )
  421. .unwrap();
  422. // Switches the program id to the token program.
  423. withdraw_ix.program_id = token_program;
  424. let tx = Transaction::new_signed_with_payer(
  425. &[withdraw_ix],
  426. Some(&context.payer.pubkey()),
  427. &[&context.payer, &wrong_owner],
  428. context.last_blockhash,
  429. );
  430. let error = context
  431. .banks_client
  432. .process_transaction(tx)
  433. .await
  434. .unwrap_err();
  435. // The we expect an error.
  436. assert_matches!(
  437. error,
  438. BanksClientError::TransactionError(TransactionError::InstructionError(
  439. _,
  440. InstructionError::Custom(4) // TokenError::OwnerMismatch
  441. ))
  442. );
  443. }
  444. #[test_case::test_case(TOKEN_PROGRAM_ID ; "p-token")]
  445. #[tokio::test]
  446. async fn fail_withdraw_excess_lamports_from_multisig_wrong_authority(token_program: Pubkey) {
  447. let context = ProgramTest::new("pinocchio_token_program", TOKEN_PROGRAM_ID, None)
  448. .start_with_context()
  449. .await;
  450. let excess_lamports = 4_000_000_000_000;
  451. // Given an account
  452. let multisig = Keypair::new();
  453. let signer1 = Keypair::new();
  454. let signer1_pubkey = signer1.pubkey();
  455. let signer2 = Keypair::new();
  456. let signer2_pubkey = signer2.pubkey();
  457. let signer3 = Keypair::new();
  458. let signer3_pubkey = signer3.pubkey();
  459. let signers = vec![&signer1_pubkey, &signer2_pubkey, &signer3_pubkey];
  460. let rent = context.banks_client.get_rent().await.unwrap();
  461. let account_size = size_of::<Multisig>();
  462. let mut initialize_ix = spl_token::instruction::initialize_multisig(
  463. &spl_token::ID,
  464. &multisig.pubkey(),
  465. &signers,
  466. 3,
  467. )
  468. .unwrap();
  469. // Switches the program id to the token program.
  470. initialize_ix.program_id = token_program;
  471. // And we initialize the multisig account.
  472. let instructions = vec![
  473. system_instruction::create_account(
  474. &context.payer.pubkey(),
  475. &multisig.pubkey(),
  476. rent.minimum_balance(account_size) + excess_lamports,
  477. account_size as u64,
  478. &token_program,
  479. ),
  480. initialize_ix,
  481. ];
  482. let tx = Transaction::new_signed_with_payer(
  483. &instructions,
  484. Some(&context.payer.pubkey()),
  485. &[&context.payer, &multisig],
  486. context.last_blockhash,
  487. );
  488. context.banks_client.process_transaction(tx).await.unwrap();
  489. let account = context
  490. .banks_client
  491. .get_account(multisig.pubkey())
  492. .await
  493. .unwrap();
  494. assert!(account.is_some());
  495. let account = account.unwrap();
  496. assert_eq!(
  497. account.lamports,
  498. rent.minimum_balance(account_size) + excess_lamports
  499. );
  500. // When we try to withdraw the excess lamports with the wrong authority.
  501. let destination = Pubkey::new_unique();
  502. let wrong_authority = Keypair::new();
  503. let mut withdraw_ix = spl_token_2022::instruction::withdraw_excess_lamports(
  504. &spl_token_2022::ID,
  505. &multisig.pubkey(),
  506. &destination,
  507. &wrong_authority.pubkey(),
  508. &signers,
  509. )
  510. .unwrap();
  511. // Switches the program id to the token program.
  512. withdraw_ix.program_id = token_program;
  513. let tx = Transaction::new_signed_with_payer(
  514. &[withdraw_ix],
  515. Some(&context.payer.pubkey()),
  516. &[&context.payer, &signer1, &signer2, &signer3],
  517. context.last_blockhash,
  518. );
  519. let error = context
  520. .banks_client
  521. .process_transaction(tx)
  522. .await
  523. .unwrap_err();
  524. // The we expect an error.
  525. assert_matches!(
  526. error,
  527. BanksClientError::TransactionError(TransactionError::InstructionError(
  528. _,
  529. InstructionError::Custom(4) // TokenError::OwnerMismatch
  530. ))
  531. );
  532. }
  533. #[test_case::test_case(TOKEN_PROGRAM_ID ; "p-token")]
  534. #[tokio::test]
  535. async fn fail_withdraw_excess_lamports_from_multisig_missing_signer(token_program: Pubkey) {
  536. let context = ProgramTest::new("pinocchio_token_program", TOKEN_PROGRAM_ID, None)
  537. .start_with_context()
  538. .await;
  539. let excess_lamports = 4_000_000_000_000;
  540. // Given an account
  541. let multisig = Keypair::new();
  542. let signer1 = Keypair::new();
  543. let signer1_pubkey = signer1.pubkey();
  544. let signer2 = Keypair::new();
  545. let signer2_pubkey = signer2.pubkey();
  546. let signer3 = Keypair::new();
  547. let signer3_pubkey = signer3.pubkey();
  548. let signers = vec![&signer1_pubkey, &signer2_pubkey, &signer3_pubkey];
  549. let rent = context.banks_client.get_rent().await.unwrap();
  550. let account_size = size_of::<Multisig>();
  551. let mut initialize_ix = spl_token::instruction::initialize_multisig(
  552. &spl_token::ID,
  553. &multisig.pubkey(),
  554. &signers,
  555. 3,
  556. )
  557. .unwrap();
  558. // Switches the program id to the token program.
  559. initialize_ix.program_id = token_program;
  560. // And we initialize the multisig account.
  561. let instructions = vec![
  562. system_instruction::create_account(
  563. &context.payer.pubkey(),
  564. &multisig.pubkey(),
  565. rent.minimum_balance(account_size) + excess_lamports,
  566. account_size as u64,
  567. &token_program,
  568. ),
  569. initialize_ix,
  570. ];
  571. let tx = Transaction::new_signed_with_payer(
  572. &instructions,
  573. Some(&context.payer.pubkey()),
  574. &[&context.payer, &multisig],
  575. context.last_blockhash,
  576. );
  577. context.banks_client.process_transaction(tx).await.unwrap();
  578. let account = context
  579. .banks_client
  580. .get_account(multisig.pubkey())
  581. .await
  582. .unwrap();
  583. assert!(account.is_some());
  584. let account = account.unwrap();
  585. assert_eq!(
  586. account.lamports,
  587. rent.minimum_balance(account_size) + excess_lamports
  588. );
  589. // When we try to withdraw the excess lamports with the wrong authority.
  590. let destination = Pubkey::new_unique();
  591. let mut withdraw_ix = spl_token_2022::instruction::withdraw_excess_lamports(
  592. &spl_token_2022::ID,
  593. &multisig.pubkey(),
  594. &destination,
  595. &multisig.pubkey(),
  596. &[&signer1_pubkey, &signer2_pubkey],
  597. )
  598. .unwrap();
  599. // Switches the program id to the token program.
  600. withdraw_ix.program_id = token_program;
  601. let tx = Transaction::new_signed_with_payer(
  602. &[withdraw_ix],
  603. Some(&context.payer.pubkey()),
  604. &[&context.payer, &signer1, &signer2],
  605. context.last_blockhash,
  606. );
  607. let error = context
  608. .banks_client
  609. .process_transaction(tx)
  610. .await
  611. .unwrap_err();
  612. // The we expect an error.
  613. assert_matches!(
  614. error,
  615. BanksClientError::TransactionError(TransactionError::InstructionError(
  616. _,
  617. InstructionError::MissingRequiredSignature
  618. ))
  619. );
  620. }