instruction.rs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708
  1. //! Instruction types
  2. use crate::error::TokenError;
  3. use solana_sdk::{
  4. instruction::{AccountMeta, Instruction},
  5. program_error::ProgramError,
  6. pubkey::Pubkey,
  7. };
  8. use std::mem::size_of;
  9. /// Minimum number of multisignature signers (min N)
  10. pub const MIN_SIGNERS: usize = 1;
  11. /// Maximum number of multisignature signers (max N)
  12. pub const MAX_SIGNERS: usize = 11;
  13. /// Instructions supported by the token program.
  14. #[repr(C)]
  15. #[derive(Clone, Debug, PartialEq)]
  16. pub enum TokenInstruction {
  17. /// Initializes a new mint and optionally deposits all the newly minted tokens in an account.
  18. ///
  19. /// The `InitializeMint` instruction requires no signers and MUST be included within
  20. /// the same Transaction as the system program's `CreateInstruction` that creates the account
  21. /// being initialized. Otherwise another party can acquire ownership of the uninitialized account.
  22. ///
  23. /// Accounts expected by this instruction:
  24. ///
  25. /// 0. `[writable]` The mint to initialize.
  26. /// 1.
  27. /// * If supply is non-zero: `[writable]` The account to hold all the newly minted tokens.
  28. /// * If supply is zero: `[]` The owner/multisignature of the mint.
  29. /// 2. `[]` (optional) The owner/multisignature of the mint if supply is non-zero, if
  30. /// present then further minting is supported.
  31. ///
  32. InitializeMint {
  33. /// Initial amount of tokens to mint.
  34. amount: u64,
  35. /// Number of base 10 digits to the right of the decimal place.
  36. decimals: u8,
  37. },
  38. /// Initializes a new account to hold tokens. If this account is associated with the native mint
  39. /// then the token balance of the initialized account will be equal to the amount of SOL in the account.
  40. ///
  41. /// The `InitializeAccount` instruction requires no signers and MUST be included within
  42. /// the same Transaction as the system program's `CreateInstruction` that creates the account
  43. /// being initialized. Otherwise another party can acquire ownership of the uninitialized account.
  44. ///
  45. /// Accounts expected by this instruction:
  46. ///
  47. /// 0. `[writable]` The account to initialize.
  48. /// 1. `[]` The mint this account will be associated with.
  49. /// 2. `[]` The new account's owner/multisignature.
  50. InitializeAccount,
  51. /// Initializes a multisignature account with N provided signers.
  52. ///
  53. /// Multisignature accounts can used in place of any single owner/delegate accounts in any
  54. /// token instruction that require an owner/delegate to be present. The variant field represents the
  55. /// number of signers (M) required to validate this multisignature account.
  56. ///
  57. /// The `InitializeMultisig` instruction requires no signers and MUST be included within
  58. /// the same Transaction as the system program's `CreateInstruction` that creates the account
  59. /// being initialized. Otherwise another party can acquire ownership of the uninitialized account.
  60. ///
  61. /// Accounts expected by this instruction:
  62. ///
  63. /// 0. `[writable]` The multisignature account to initialize.
  64. /// 1. ..1+N. `[]` The signer accounts, must equal to N where 1 <= N <= 11.
  65. InitializeMultisig {
  66. /// The number of signers (M) required to validate this multisignature account.
  67. m: u8,
  68. },
  69. /// Transfers tokens from one account to another either directly or via a delegate. If this
  70. /// account is associated with the native mint then equal amounts of SOL and Tokens will be
  71. /// transferred to the destination account.
  72. ///
  73. /// Accounts expected by this instruction:
  74. ///
  75. /// * Single owner/delegate
  76. /// 0. `[writable]` The source account.
  77. /// 1. `[writable]` The destination account.
  78. /// 2. '[signer]' The source account's owner/delegate.
  79. ///
  80. /// * Multisignature owner/delegate
  81. /// 0. `[writable]` The source account.
  82. /// 1. `[writable]` The destination account.
  83. /// 2. '[]' The source account's multisignature owner/delegate.
  84. /// 3. ..3+M '[signer]' M signer accounts.
  85. Transfer {
  86. /// The amount of tokens to transfer.
  87. amount: u64,
  88. },
  89. /// Approves a delegate. A delegate is given the authority over
  90. /// tokens on behalf of the source account's owner.
  91. /// Accounts expected by this instruction:
  92. ///
  93. /// * Single owner
  94. /// 0. `[writable]` The source account.
  95. /// 1. `[]` The delegate.
  96. /// 2. `[signer]` The source account owner.
  97. ///
  98. /// * Multisignature owner
  99. /// 0. `[writable]` The source account.
  100. /// 1. `[]` The delegate.
  101. /// 2. '[]' The source account's multisignature owner.
  102. /// 3. ..3+M '[signer]' M signer accounts
  103. Approve {
  104. /// The amount of tokens the delegate is approved for.
  105. amount: u64,
  106. },
  107. /// Revokes the delegate's authority.
  108. ///
  109. /// Accounts expected by this instruction:
  110. ///
  111. /// * Single owner
  112. /// 0. `[writable]` The source account.
  113. /// 1. `[signer]` The source account owner.
  114. ///
  115. /// * Multisignature owner
  116. /// 0. `[writable]` The source account.
  117. /// 1. '[]' The source account's multisignature owner.
  118. /// 2. ..2+M '[signer]' M signer accounts
  119. Revoke,
  120. /// Sets a new owner of a mint or account.
  121. ///
  122. /// Accounts expected by this instruction:
  123. ///
  124. /// * Single owner
  125. /// 0. `[writable]` The mint or account to change the owner of.
  126. /// 1. `[]` The new owner/delegate/multisignature.
  127. /// 2. `[signer]` The owner of the mint or account.
  128. ///
  129. /// * Multisignature owner
  130. /// 0. `[writable]` The mint or account to change the owner of.
  131. /// 1. `[]` The new owner/delegate/multisignature.
  132. /// 2. `[]` The mint's or account's multisignature owner.
  133. /// 3. ..3+M '[signer]' M signer accounts
  134. SetOwner,
  135. /// Mints new tokens to an account. The native mint does not support minting.
  136. ///
  137. /// Accounts expected by this instruction:
  138. ///
  139. /// * Single owner
  140. /// 0. `[writable]` The mint.
  141. /// 1. `[writable]` The account to mint tokens to.
  142. /// 2. `[signer]` The mint's owner.
  143. ///
  144. /// * Multisignature owner
  145. /// 0. `[writable]` The mint.
  146. /// 1. `[writable]` The account to mint tokens to.
  147. /// 2. `[]` The mint's multisignature owner.
  148. /// 3. ..3+M '[signer]' M signer accounts.
  149. MintTo {
  150. /// The amount of new tokens to mint.
  151. amount: u64,
  152. },
  153. /// Burns tokens by removing them from an account. `Burn` does not support accounts
  154. /// associated with the native mint, use `CloseAccount` instead.
  155. ///
  156. /// Accounts expected by this instruction:
  157. ///
  158. /// * Single owner/delegate
  159. /// 0. `[writable]` The account to burn from.
  160. /// 1. `[signer]` The account's owner/delegate.
  161. ///
  162. /// * Multisignature owner/delegate
  163. /// 0. `[writable]` The account to burn from.
  164. /// 1. `[]` The account's multisignature owner/delegate.
  165. /// 2. ..2+M '[signer]' M signer accounts.
  166. Burn {
  167. /// The amount of tokens to burn.
  168. amount: u64,
  169. },
  170. /// Close an account by transferring all its SOL to the destination account.
  171. /// Non-native accounts may only be closed if its token amount is zero.
  172. ///
  173. /// Accounts expected by this instruction:
  174. ///
  175. /// * Single owner
  176. /// 0. `[writable]` The account to close.
  177. /// 1. '[writable]' The destination account.
  178. /// 2. `[signer]` The account's owner.
  179. ///
  180. /// * Multisignature owner
  181. /// 0. `[writable]` The account to close.
  182. /// 1. '[writable]' The destination account.
  183. /// 2. `[]` The account's multisignature owner.
  184. /// 3. ..3+M '[signer]' M signer accounts.
  185. CloseAccount,
  186. }
  187. impl TokenInstruction {
  188. /// Unpacks a byte buffer into a [TokenInstruction](enum.TokenInstruction.html).
  189. pub fn unpack(input: &[u8]) -> Result<Self, ProgramError> {
  190. if input.len() < size_of::<u8>() {
  191. return Err(TokenError::InvalidInstruction.into());
  192. }
  193. Ok(match input[0] {
  194. 0 => {
  195. if input.len() < size_of::<u8>() + size_of::<u64>() + size_of::<u8>() {
  196. return Err(TokenError::InvalidInstruction.into());
  197. }
  198. #[allow(clippy::cast_ptr_alignment)]
  199. let amount = unsafe { *(&input[size_of::<u8>()] as *const u8 as *const u64) };
  200. let decimals =
  201. unsafe { *(&input[size_of::<u8>() + size_of::<u64>()] as *const u8) };
  202. Self::InitializeMint { amount, decimals }
  203. }
  204. 1 => Self::InitializeAccount,
  205. 2 => {
  206. if input.len() < size_of::<u8>() + size_of::<u8>() {
  207. return Err(TokenError::InvalidInstruction.into());
  208. }
  209. #[allow(clippy::cast_ptr_alignment)]
  210. let m = unsafe { *(&input[1] as *const u8) };
  211. Self::InitializeMultisig { m }
  212. }
  213. 3 => {
  214. if input.len() < size_of::<u8>() + size_of::<u64>() {
  215. return Err(TokenError::InvalidInstruction.into());
  216. }
  217. #[allow(clippy::cast_ptr_alignment)]
  218. let amount = unsafe { *(&input[size_of::<u8>()] as *const u8 as *const u64) };
  219. Self::Transfer { amount }
  220. }
  221. 4 => {
  222. if input.len() < size_of::<u8>() + size_of::<u64>() {
  223. return Err(TokenError::InvalidInstruction.into());
  224. }
  225. #[allow(clippy::cast_ptr_alignment)]
  226. let amount = unsafe { *(&input[size_of::<u8>()] as *const u8 as *const u64) };
  227. Self::Approve { amount }
  228. }
  229. 5 => Self::Revoke,
  230. 6 => Self::SetOwner,
  231. 7 => {
  232. if input.len() < size_of::<u8>() + size_of::<u64>() {
  233. return Err(TokenError::InvalidInstruction.into());
  234. }
  235. #[allow(clippy::cast_ptr_alignment)]
  236. let amount = unsafe { *(&input[size_of::<u8>()] as *const u8 as *const u64) };
  237. Self::MintTo { amount }
  238. }
  239. 8 => {
  240. if input.len() < size_of::<u8>() + size_of::<u64>() {
  241. return Err(TokenError::InvalidInstruction.into());
  242. }
  243. #[allow(clippy::cast_ptr_alignment)]
  244. let amount = unsafe { *(&input[size_of::<u8>()] as *const u8 as *const u64) };
  245. Self::Burn { amount }
  246. }
  247. 9 => Self::CloseAccount,
  248. _ => return Err(TokenError::InvalidInstruction.into()),
  249. })
  250. }
  251. /// Packs a [TokenInstruction](enum.TokenInstruction.html) into a byte buffer.
  252. pub fn pack(&self) -> Result<Vec<u8>, ProgramError> {
  253. let mut output = vec![0u8; size_of::<TokenInstruction>()];
  254. let mut output_len = 0;
  255. match self {
  256. Self::InitializeMint { amount, decimals } => {
  257. output[output_len] = 0;
  258. output_len += size_of::<u8>();
  259. #[allow(clippy::cast_ptr_alignment)]
  260. let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) };
  261. *value = *amount;
  262. output_len += size_of::<u64>();
  263. let value = unsafe { &mut *(&mut output[output_len] as *mut u8) };
  264. *value = *decimals;
  265. output_len += size_of::<u8>();
  266. }
  267. Self::InitializeAccount => {
  268. output[output_len] = 1;
  269. output_len += size_of::<u8>();
  270. }
  271. Self::InitializeMultisig { m } => {
  272. output[output_len] = 2;
  273. output_len += size_of::<u8>();
  274. #[allow(clippy::cast_ptr_alignment)]
  275. let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u8) };
  276. *value = *m;
  277. output_len += size_of::<u8>();
  278. }
  279. Self::Transfer { amount } => {
  280. output[output_len] = 3;
  281. output_len += size_of::<u8>();
  282. #[allow(clippy::cast_ptr_alignment)]
  283. let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) };
  284. *value = *amount;
  285. output_len += size_of::<u64>();
  286. }
  287. Self::Approve { amount } => {
  288. output[output_len] = 4;
  289. output_len += size_of::<u8>();
  290. #[allow(clippy::cast_ptr_alignment)]
  291. let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) };
  292. *value = *amount;
  293. output_len += size_of::<u64>();
  294. }
  295. Self::Revoke => {
  296. output[output_len] = 5;
  297. output_len += size_of::<u8>();
  298. }
  299. Self::SetOwner => {
  300. output[output_len] = 6;
  301. output_len += size_of::<u8>();
  302. }
  303. Self::MintTo { amount } => {
  304. output[output_len] = 7;
  305. output_len += size_of::<u8>();
  306. #[allow(clippy::cast_ptr_alignment)]
  307. let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) };
  308. *value = *amount;
  309. output_len += size_of::<u64>();
  310. }
  311. Self::Burn { amount } => {
  312. output[output_len] = 8;
  313. output_len += size_of::<u8>();
  314. #[allow(clippy::cast_ptr_alignment)]
  315. let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) };
  316. *value = *amount;
  317. output_len += size_of::<u64>();
  318. }
  319. Self::CloseAccount => {
  320. output[output_len] = 9;
  321. output_len += size_of::<u8>();
  322. }
  323. }
  324. output.truncate(output_len);
  325. Ok(output)
  326. }
  327. }
  328. /// Creates a 'InitializeMint' instruction.
  329. pub fn initialize_mint(
  330. token_program_id: &Pubkey,
  331. mint_pubkey: &Pubkey,
  332. account_pubkey: Option<&Pubkey>,
  333. owner_pubkey: Option<&Pubkey>,
  334. amount: u64,
  335. decimals: u8,
  336. ) -> Result<Instruction, ProgramError> {
  337. let data = TokenInstruction::InitializeMint { amount, decimals }.pack()?;
  338. let mut accounts = vec![AccountMeta::new(*mint_pubkey, false)];
  339. if amount != 0 {
  340. match account_pubkey {
  341. Some(pubkey) => accounts.push(AccountMeta::new(*pubkey, false)),
  342. None => {
  343. return Err(ProgramError::NotEnoughAccountKeys);
  344. }
  345. }
  346. }
  347. match owner_pubkey {
  348. Some(pubkey) => accounts.push(AccountMeta::new_readonly(*pubkey, false)),
  349. None => {
  350. if amount == 0 {
  351. return Err(TokenError::OwnerRequiredIfNoInitialSupply.into());
  352. }
  353. }
  354. }
  355. Ok(Instruction {
  356. program_id: *token_program_id,
  357. accounts,
  358. data,
  359. })
  360. }
  361. /// Creates a `InitializeAccount` instruction.
  362. pub fn initialize_account(
  363. token_program_id: &Pubkey,
  364. account_pubkey: &Pubkey,
  365. mint_pubkey: &Pubkey,
  366. owner_pubkey: &Pubkey,
  367. ) -> Result<Instruction, ProgramError> {
  368. let data = TokenInstruction::InitializeAccount.pack()?;
  369. let accounts = vec![
  370. AccountMeta::new(*account_pubkey, false),
  371. AccountMeta::new_readonly(*mint_pubkey, false),
  372. AccountMeta::new_readonly(*owner_pubkey, false),
  373. ];
  374. Ok(Instruction {
  375. program_id: *token_program_id,
  376. accounts,
  377. data,
  378. })
  379. }
  380. /// Creates a `InitializeMultisig` instruction.
  381. pub fn initialize_multisig(
  382. token_program_id: &Pubkey,
  383. multisig_pubkey: &Pubkey,
  384. signer_pubkeys: &[&Pubkey],
  385. m: u8,
  386. ) -> Result<Instruction, ProgramError> {
  387. if !is_valid_signer_index(m as usize)
  388. || !is_valid_signer_index(signer_pubkeys.len())
  389. || m as usize > signer_pubkeys.len()
  390. {
  391. return Err(ProgramError::MissingRequiredSignature);
  392. }
  393. let data = TokenInstruction::InitializeMultisig { m }.pack()?;
  394. let mut accounts = Vec::with_capacity(1 + signer_pubkeys.len());
  395. accounts.push(AccountMeta::new(*multisig_pubkey, false));
  396. for signer_pubkey in signer_pubkeys.iter() {
  397. accounts.push(AccountMeta::new_readonly(**signer_pubkey, false));
  398. }
  399. Ok(Instruction {
  400. program_id: *token_program_id,
  401. accounts,
  402. data,
  403. })
  404. }
  405. /// Creates a `Transfer` instruction.
  406. pub fn transfer(
  407. token_program_id: &Pubkey,
  408. source_pubkey: &Pubkey,
  409. destination_pubkey: &Pubkey,
  410. authority_pubkey: &Pubkey,
  411. signer_pubkeys: &[&Pubkey],
  412. amount: u64,
  413. ) -> Result<Instruction, ProgramError> {
  414. let data = TokenInstruction::Transfer { amount }.pack()?;
  415. let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
  416. accounts.push(AccountMeta::new(*source_pubkey, false));
  417. accounts.push(AccountMeta::new(*destination_pubkey, false));
  418. accounts.push(AccountMeta::new_readonly(
  419. *authority_pubkey,
  420. signer_pubkeys.is_empty(),
  421. ));
  422. for signer_pubkey in signer_pubkeys.iter() {
  423. accounts.push(AccountMeta::new(**signer_pubkey, true));
  424. }
  425. Ok(Instruction {
  426. program_id: *token_program_id,
  427. accounts,
  428. data,
  429. })
  430. }
  431. /// Creates an `Approve` instruction.
  432. pub fn approve(
  433. token_program_id: &Pubkey,
  434. source_pubkey: &Pubkey,
  435. delegate_pubkey: &Pubkey,
  436. owner_pubkey: &Pubkey,
  437. signer_pubkeys: &[&Pubkey],
  438. amount: u64,
  439. ) -> Result<Instruction, ProgramError> {
  440. let data = TokenInstruction::Approve { amount }.pack()?;
  441. let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
  442. accounts.push(AccountMeta::new(*source_pubkey, false));
  443. accounts.push(AccountMeta::new_readonly(*delegate_pubkey, false));
  444. accounts.push(AccountMeta::new_readonly(
  445. *owner_pubkey,
  446. signer_pubkeys.is_empty(),
  447. ));
  448. for signer_pubkey in signer_pubkeys.iter() {
  449. accounts.push(AccountMeta::new(**signer_pubkey, true));
  450. }
  451. Ok(Instruction {
  452. program_id: *token_program_id,
  453. accounts,
  454. data,
  455. })
  456. }
  457. /// Creates a `Revoke` instruction.
  458. pub fn revoke(
  459. token_program_id: &Pubkey,
  460. source_pubkey: &Pubkey,
  461. owner_pubkey: &Pubkey,
  462. signer_pubkeys: &[&Pubkey],
  463. ) -> Result<Instruction, ProgramError> {
  464. let data = TokenInstruction::Revoke.pack()?;
  465. let mut accounts = Vec::with_capacity(2 + signer_pubkeys.len());
  466. accounts.push(AccountMeta::new_readonly(*source_pubkey, false));
  467. accounts.push(AccountMeta::new_readonly(
  468. *owner_pubkey,
  469. signer_pubkeys.is_empty(),
  470. ));
  471. for signer_pubkey in signer_pubkeys.iter() {
  472. accounts.push(AccountMeta::new(**signer_pubkey, true));
  473. }
  474. Ok(Instruction {
  475. program_id: *token_program_id,
  476. accounts,
  477. data,
  478. })
  479. }
  480. /// Creates a `SetOwner` instruction.
  481. pub fn set_owner(
  482. token_program_id: &Pubkey,
  483. owned_pubkey: &Pubkey,
  484. new_owner_pubkey: &Pubkey,
  485. owner_pubkey: &Pubkey,
  486. signer_pubkeys: &[&Pubkey],
  487. ) -> Result<Instruction, ProgramError> {
  488. let data = TokenInstruction::SetOwner.pack()?;
  489. let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
  490. accounts.push(AccountMeta::new(*owned_pubkey, false));
  491. accounts.push(AccountMeta::new_readonly(*new_owner_pubkey, false));
  492. accounts.push(AccountMeta::new_readonly(
  493. *owner_pubkey,
  494. signer_pubkeys.is_empty(),
  495. ));
  496. for signer_pubkey in signer_pubkeys.iter() {
  497. accounts.push(AccountMeta::new(**signer_pubkey, true));
  498. }
  499. Ok(Instruction {
  500. program_id: *token_program_id,
  501. accounts,
  502. data,
  503. })
  504. }
  505. /// Creates a `MintTo` instruction.
  506. pub fn mint_to(
  507. token_program_id: &Pubkey,
  508. mint_pubkey: &Pubkey,
  509. account_pubkey: &Pubkey,
  510. owner_pubkey: &Pubkey,
  511. signer_pubkeys: &[&Pubkey],
  512. amount: u64,
  513. ) -> Result<Instruction, ProgramError> {
  514. let data = TokenInstruction::MintTo { amount }.pack()?;
  515. let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
  516. accounts.push(AccountMeta::new(*mint_pubkey, false));
  517. accounts.push(AccountMeta::new(*account_pubkey, false));
  518. accounts.push(AccountMeta::new_readonly(
  519. *owner_pubkey,
  520. signer_pubkeys.is_empty(),
  521. ));
  522. for signer_pubkey in signer_pubkeys.iter() {
  523. accounts.push(AccountMeta::new(**signer_pubkey, true));
  524. }
  525. Ok(Instruction {
  526. program_id: *token_program_id,
  527. accounts,
  528. data,
  529. })
  530. }
  531. /// Creates a `Burn` instruction.
  532. pub fn burn(
  533. token_program_id: &Pubkey,
  534. account_pubkey: &Pubkey,
  535. authority_pubkey: &Pubkey,
  536. signer_pubkeys: &[&Pubkey],
  537. amount: u64,
  538. ) -> Result<Instruction, ProgramError> {
  539. let data = TokenInstruction::Burn { amount }.pack()?;
  540. let mut accounts = Vec::with_capacity(2 + signer_pubkeys.len());
  541. accounts.push(AccountMeta::new(*account_pubkey, false));
  542. accounts.push(AccountMeta::new_readonly(
  543. *authority_pubkey,
  544. signer_pubkeys.is_empty(),
  545. ));
  546. for signer_pubkey in signer_pubkeys.iter() {
  547. accounts.push(AccountMeta::new(**signer_pubkey, true));
  548. }
  549. Ok(Instruction {
  550. program_id: *token_program_id,
  551. accounts,
  552. data,
  553. })
  554. }
  555. /// Creates a `CloseAccount` instruction.
  556. pub fn close_account(
  557. token_program_id: &Pubkey,
  558. account_pubkey: &Pubkey,
  559. destination_pubkey: &Pubkey,
  560. owner_pubkey: &Pubkey,
  561. signer_pubkeys: &[&Pubkey],
  562. ) -> Result<Instruction, ProgramError> {
  563. let data = TokenInstruction::CloseAccount.pack()?;
  564. let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
  565. accounts.push(AccountMeta::new(*account_pubkey, false));
  566. accounts.push(AccountMeta::new(*destination_pubkey, false));
  567. accounts.push(AccountMeta::new_readonly(
  568. *owner_pubkey,
  569. signer_pubkeys.is_empty(),
  570. ));
  571. for signer_pubkey in signer_pubkeys.iter() {
  572. accounts.push(AccountMeta::new(**signer_pubkey, true));
  573. }
  574. Ok(Instruction {
  575. program_id: *token_program_id,
  576. accounts,
  577. data,
  578. })
  579. }
  580. /// Utility function that checks index is between MIN_SIGNERS and MAX_SIGNERS
  581. pub fn is_valid_signer_index(index: usize) -> bool {
  582. !(index < MIN_SIGNERS || index > MAX_SIGNERS)
  583. }
  584. #[cfg(test)]
  585. mod test {
  586. use super::*;
  587. #[test]
  588. fn test_instruction_packing() {
  589. let check = TokenInstruction::InitializeMint {
  590. amount: 1,
  591. decimals: 2,
  592. };
  593. let packed = check.pack().unwrap();
  594. let expect = Vec::from([0u8, 1, 0, 0, 0, 0, 0, 0, 0, 2]);
  595. assert_eq!(packed, expect);
  596. let unpacked = TokenInstruction::unpack(&expect).unwrap();
  597. assert_eq!(unpacked, check);
  598. let check = TokenInstruction::InitializeAccount;
  599. let packed = check.pack().unwrap();
  600. let expect = Vec::from([1u8]);
  601. assert_eq!(packed, expect);
  602. let unpacked = TokenInstruction::unpack(&expect).unwrap();
  603. assert_eq!(unpacked, check);
  604. let check = TokenInstruction::InitializeMultisig { m: 1 };
  605. let packed = check.pack().unwrap();
  606. let expect = Vec::from([2u8, 1]);
  607. assert_eq!(packed, expect);
  608. let unpacked = TokenInstruction::unpack(&expect).unwrap();
  609. assert_eq!(unpacked, check);
  610. let check = TokenInstruction::Transfer { amount: 1 };
  611. let packed = check.pack().unwrap();
  612. let expect = Vec::from([3u8, 1, 0, 0, 0, 0, 0, 0, 0]);
  613. assert_eq!(packed, expect);
  614. let unpacked = TokenInstruction::unpack(&expect).unwrap();
  615. assert_eq!(unpacked, check);
  616. let check = TokenInstruction::Approve { amount: 1 };
  617. let packed = check.pack().unwrap();
  618. let expect = Vec::from([4u8, 1, 0, 0, 0, 0, 0, 0, 0]);
  619. assert_eq!(packed, expect);
  620. let unpacked = TokenInstruction::unpack(&expect).unwrap();
  621. assert_eq!(unpacked, check);
  622. let check = TokenInstruction::Revoke;
  623. let packed = check.pack().unwrap();
  624. let expect = Vec::from([5u8]);
  625. assert_eq!(packed, expect);
  626. let unpacked = TokenInstruction::unpack(&expect).unwrap();
  627. assert_eq!(unpacked, check);
  628. let check = TokenInstruction::SetOwner;
  629. let packed = check.pack().unwrap();
  630. let expect = Vec::from([6u8]);
  631. assert_eq!(packed, expect);
  632. let unpacked = TokenInstruction::unpack(&expect).unwrap();
  633. assert_eq!(unpacked, check);
  634. let check = TokenInstruction::MintTo { amount: 1 };
  635. let packed = check.pack().unwrap();
  636. let expect = Vec::from([7u8, 1, 0, 0, 0, 0, 0, 0, 0]);
  637. assert_eq!(packed, expect);
  638. let unpacked = TokenInstruction::unpack(&expect).unwrap();
  639. assert_eq!(unpacked, check);
  640. let check = TokenInstruction::Burn { amount: 1 };
  641. let packed = check.pack().unwrap();
  642. let expect = Vec::from([8u8, 1, 0, 0, 0, 0, 0, 0, 0]);
  643. assert_eq!(packed, expect);
  644. let unpacked = TokenInstruction::unpack(&expect).unwrap();
  645. assert_eq!(unpacked, check);
  646. let check = TokenInstruction::CloseAccount;
  647. let packed = check.pack().unwrap();
  648. let expect = Vec::from([9u8]);
  649. assert_eq!(packed, expect);
  650. let unpacked = TokenInstruction::unpack(&expect).unwrap();
  651. assert_eq!(unpacked, check);
  652. }
  653. }