From 84f7d8286843c780340b3c4ea19b1c0d2a1c9253 Mon Sep 17 00:00:00 2001 From: NagaprasadVr Date: Tue, 12 Nov 2024 10:14:50 +0530 Subject: [PATCH 01/13] add IxData struct to handle write and read bytes --- programs/token/src/instructions/approve.rs | 14 ++-- .../token/src/instructions/approve_checked.rs | 15 ++-- programs/token/src/instructions/burn.rs | 13 ++-- .../token/src/instructions/burn_checked.rs | 15 ++-- .../src/instructions/initialize_account_2.rs | 11 ++- .../src/instructions/initialize_account_3.rs | 10 +-- .../token/src/instructions/initialize_mint.rs | 19 ++--- .../src/instructions/initialize_mint_2.rs | 19 ++--- programs/token/src/instructions/mint_to.rs | 11 ++- .../token/src/instructions/mint_to_checked.rs | 12 ++- .../token/src/instructions/set_authority.rs | 17 ++--- programs/token/src/instructions/transfer.rs | 10 +-- .../src/instructions/transfer_checked.rs | 12 ++- programs/token/src/lib.rs | 75 +++++++++++++++++++ 14 files changed, 147 insertions(+), 106 deletions(-) diff --git a/programs/token/src/instructions/approve.rs b/programs/token/src/instructions/approve.rs index d9596f7..c0c3e02 100644 --- a/programs/token/src/instructions/approve.rs +++ b/programs/token/src/instructions/approve.rs @@ -1,5 +1,3 @@ -use core::slice::from_raw_parts; - use pinocchio::{ account_info::AccountInfo, instruction::{AccountMeta, Instruction, Signer}, @@ -7,7 +5,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{write_bytes, UNINIT_BYTE}; +use crate::{IxData, UNINIT_BYTE}; /// Approves a delegate. /// @@ -43,17 +41,19 @@ impl<'a> Approve<'a> { // Instruction data // - [0]: instruction discriminator (1 byte, u8) // - [1..9]: amount (8 bytes, u64) - let mut instruction_data = [UNINIT_BYTE; 9]; + let mut ix_buffer = [UNINIT_BYTE; 9]; + let mut ix_data = IxData::new(&mut ix_buffer); // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[4]); + ix_data.write_bytes(&[4]); // Set amount as u64 at offset [1..9] - write_bytes(&mut instruction_data[1..], &self.amount.to_le_bytes()); + // write_bytes(&mut instruction_data[1..], &self.amount.to_le_bytes()); + ix_data.write_bytes(&self.amount.to_le_bytes()); let instruction = Instruction { program_id: &crate::ID, accounts: &account_metas, - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 9) }, + data: ix_data.read_bytes(), }; invoke_signed( diff --git a/programs/token/src/instructions/approve_checked.rs b/programs/token/src/instructions/approve_checked.rs index 6195122..50da319 100644 --- a/programs/token/src/instructions/approve_checked.rs +++ b/programs/token/src/instructions/approve_checked.rs @@ -1,5 +1,3 @@ -use core::slice::from_raw_parts; - use pinocchio::{ account_info::AccountInfo, instruction::{AccountMeta, Instruction, Signer}, @@ -7,7 +5,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{write_bytes, UNINIT_BYTE}; +use crate::{IxData, UNINIT_BYTE}; /// Approves a delegate. /// @@ -50,19 +48,20 @@ impl<'a> ApproveChecked<'a> { // - [0] : instruction discriminator (1 byte, u8) // - [1..9]: amount (8 bytes, u64) // - [9] : decimals (1 byte, u8) - let mut instruction_data = [UNINIT_BYTE; 10]; + let mut ix_buffer = [UNINIT_BYTE; 10]; + let mut ix_data = IxData::new(&mut ix_buffer); // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[13]); + ix_data.write_bytes(&[13]); // Set amount as u64 at offset [1..9] - write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes()); + ix_data.write_bytes(&self.amount.to_le_bytes()); // Set decimals as u8 at offset [9] - write_bytes(&mut instruction_data[9..], &[self.decimals]); + ix_data.write_bytes(&[self.decimals]); let instruction = Instruction { program_id: &crate::ID, accounts: &account_metas, - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 10) }, + data: ix_data.read_bytes(), }; invoke_signed( diff --git a/programs/token/src/instructions/burn.rs b/programs/token/src/instructions/burn.rs index 4a8e71a..290d6ba 100644 --- a/programs/token/src/instructions/burn.rs +++ b/programs/token/src/instructions/burn.rs @@ -1,5 +1,3 @@ -use core::slice::from_raw_parts; - use pinocchio::{ account_info::AccountInfo, instruction::{AccountMeta, Instruction, Signer}, @@ -7,7 +5,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{write_bytes, UNINIT_BYTE}; +use crate::{IxData, UNINIT_BYTE}; /// Burns tokens by removing them from an account. /// @@ -43,17 +41,18 @@ impl<'a> Burn<'a> { // Instruction data // - [0]: instruction discriminator (1 byte, u8) // - [1..9]: amount (8 bytes, u64) - let mut instruction_data = [UNINIT_BYTE; 9]; + let mut ix_buffer = [UNINIT_BYTE; 9]; + let mut ix_data = IxData::new(&mut ix_buffer); // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[8]); + ix_data.write_bytes(&[8]); // Set amount as u64 at offset [1..9] - write_bytes(&mut instruction_data[1..], &self.amount.to_le_bytes()); + ix_data.write_bytes(&self.amount.to_le_bytes()); let instruction = Instruction { program_id: &crate::ID, accounts: &account_metas, - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 9) }, + data: ix_data.read_bytes(), }; invoke_signed( diff --git a/programs/token/src/instructions/burn_checked.rs b/programs/token/src/instructions/burn_checked.rs index 789a320..fa820bd 100644 --- a/programs/token/src/instructions/burn_checked.rs +++ b/programs/token/src/instructions/burn_checked.rs @@ -1,6 +1,4 @@ -use core::slice::from_raw_parts; - -use crate::{write_bytes, UNINIT_BYTE}; +use crate::{IxData, UNINIT_BYTE}; use pinocchio::{ account_info::AccountInfo, instruction::{AccountMeta, Instruction, Signer}, @@ -45,19 +43,20 @@ impl<'a> BurnChecked<'a> { // - [0]: instruction discriminator (1 byte, u8) // - [1..9]: amount (8 bytes, u64) // - [9]: decimals (1 byte, u8) - let mut instruction_data = [UNINIT_BYTE; 10]; + let mut ix_buffer = [UNINIT_BYTE; 10]; + let mut ix_data = IxData::new(&mut ix_buffer); // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[15]); + ix_data.write_bytes(&[15]); // Set amount as u64 at offset [1..9] - write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes()); + ix_data.write_bytes(&self.amount.to_le_bytes()); // Set decimals as u8 at offset [9] - write_bytes(&mut instruction_data[9..], &[self.decimals]); + ix_data.write_bytes(&[self.decimals]); let instruction = Instruction { program_id: &crate::ID, accounts: &account_metas, - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 10) }, + data: ix_data.read_bytes(), }; invoke_signed( diff --git a/programs/token/src/instructions/initialize_account_2.rs b/programs/token/src/instructions/initialize_account_2.rs index 9ca72c7..5c1b529 100644 --- a/programs/token/src/instructions/initialize_account_2.rs +++ b/programs/token/src/instructions/initialize_account_2.rs @@ -1,5 +1,3 @@ -use core::slice::from_raw_parts; - use pinocchio::{ account_info::AccountInfo, instruction::{AccountMeta, Instruction, Signer}, @@ -8,7 +6,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{write_bytes, UNINIT_BYTE}; +use crate::{IxData, UNINIT_BYTE}; /// Initialize a new Token Account. /// @@ -47,14 +45,15 @@ impl<'a> InitilizeAccount2<'a> { let mut instruction_data = [UNINIT_BYTE; 33]; // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[16]); + ix_data.write_bytes(&[16]); + // Set owner as [u8; 32] at offset [1..33] - write_bytes(&mut instruction_data[1..], self.owner); + ix_data.write_bytes(self.owner.as_ref()); let instruction = Instruction { program_id: &crate::ID, accounts: &account_metas, - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 33) }, + data: ix_data.read_bytes(), }; invoke_signed( diff --git a/programs/token/src/instructions/initialize_account_3.rs b/programs/token/src/instructions/initialize_account_3.rs index f3dc433..4fac6d6 100644 --- a/programs/token/src/instructions/initialize_account_3.rs +++ b/programs/token/src/instructions/initialize_account_3.rs @@ -1,5 +1,3 @@ -use core::slice::from_raw_parts; - use pinocchio::{ account_info::AccountInfo, instruction::{AccountMeta, Instruction, Signer}, @@ -8,7 +6,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{write_bytes, UNINIT_BYTE}; +use crate::{IxData, UNINIT_BYTE}; /// Initialize a new Token Account. /// @@ -43,14 +41,14 @@ impl<'a> InitilizeAccount3<'a> { let mut instruction_data = [UNINIT_BYTE; 33]; // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[18]); + ix_data.write_bytes(&[18]); // Set owner as [u8; 32] at offset [1..33] - write_bytes(&mut instruction_data[1..], self.owner); + ix_data.write_bytes(self.owner.as_ref()); let instruction = Instruction { program_id: &crate::ID, accounts: &account_metas, - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 33) }, + data: ix_data.read_bytes(), }; invoke_signed(&instruction, &[self.token, self.mint], signers) diff --git a/programs/token/src/instructions/initialize_mint.rs b/programs/token/src/instructions/initialize_mint.rs index 3cf1fa3..8976bbe 100644 --- a/programs/token/src/instructions/initialize_mint.rs +++ b/programs/token/src/instructions/initialize_mint.rs @@ -1,5 +1,3 @@ -use core::slice::from_raw_parts; - use pinocchio::{ account_info::AccountInfo, instruction::{AccountMeta, Instruction, Signer}, @@ -8,7 +6,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{write_bytes, UNINIT_BYTE}; +use crate::{IxData, UNINIT_BYTE}; /// Initialize a new mint. /// @@ -50,23 +48,18 @@ impl<'a> InitilizeMint<'a> { let mut instruction_data = [UNINIT_BYTE; 67]; // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[0]); + ix_data.write_bytes(&[0]); // Set decimals as u8 at offset [1] - write_bytes(&mut instruction_data[1..2], &[self.decimals]); + ix_data.write_bytes(&[self.decimals]); // Set mint_authority as Pubkey at offset [2..34] - write_bytes(&mut instruction_data[2..34], self.mint_authority); + ix_data.write_bytes(self.mint_authority.as_ref()); // Set COption & freeze_authority at offset [34..67] - if let Some(freeze_auth) = self.freeze_authority { - write_bytes(&mut instruction_data[34..35], &[1]); - write_bytes(&mut instruction_data[35..], freeze_auth); - } else { - write_bytes(&mut instruction_data[34..35], &[0]); - } + ix_data.write_optional_pubkey_bytes(self.freeze_authority); let instruction = Instruction { program_id: &crate::ID, accounts: &account_metas, - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 67) }, + data: ix_data.read_bytes(), }; invoke_signed(&instruction, &[self.mint, self.rent_sysvar], signers) diff --git a/programs/token/src/instructions/initialize_mint_2.rs b/programs/token/src/instructions/initialize_mint_2.rs index 51954be..24487e3 100644 --- a/programs/token/src/instructions/initialize_mint_2.rs +++ b/programs/token/src/instructions/initialize_mint_2.rs @@ -1,5 +1,3 @@ -use core::slice::from_raw_parts; - use pinocchio::{ account_info::AccountInfo, instruction::{AccountMeta, Instruction, Signer}, @@ -8,7 +6,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{write_bytes, UNINIT_BYTE}; +use crate::{IxData, UNINIT_BYTE}; /// Initialize a new mint. /// @@ -44,23 +42,18 @@ impl<'a> InitilizeMint2<'a> { let mut instruction_data = [UNINIT_BYTE; 67]; // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[20]); + ix_data.write_bytes(&[20]); // Set decimals as u8 at offset [1] - write_bytes(&mut instruction_data[1..2], &[self.decimals]); + ix_data.write_bytes(&[self.decimals]); // Set mint_authority as Pubkey at offset [2..34] - write_bytes(&mut instruction_data[2..34], self.mint_authority); + ix_data.write_bytes(self.mint_authority.as_ref()); // Set COption & freeze_authority at offset [34..67] - if let Some(freeze_auth) = self.freeze_authority { - write_bytes(&mut instruction_data[34..35], &[1]); - write_bytes(&mut instruction_data[35..], freeze_auth); - } else { - write_bytes(&mut instruction_data[34..35], &[0]); - } + ix_data.write_optional_pubkey_bytes(self.freeze_authority); let instruction = Instruction { program_id: &crate::ID, accounts: &account_metas, - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 67) }, + data: ix_data.read_bytes(), }; invoke_signed(&instruction, &[self.mint], signers) diff --git a/programs/token/src/instructions/mint_to.rs b/programs/token/src/instructions/mint_to.rs index e0036db..5325b56 100644 --- a/programs/token/src/instructions/mint_to.rs +++ b/programs/token/src/instructions/mint_to.rs @@ -1,5 +1,3 @@ -use core::slice::from_raw_parts; - use pinocchio::{ account_info::AccountInfo, instruction::{AccountMeta, Instruction, Signer}, @@ -7,7 +5,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{write_bytes, UNINIT_BYTE}; +use crate::{IxData, UNINIT_BYTE}; /// Mints new tokens to an account. /// @@ -47,14 +45,15 @@ impl<'a> MintTo<'a> { let mut instruction_data = [UNINIT_BYTE; 9]; // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[7]); + ix_data.write_bytes(&[7]); + // Set amount as u64 at offset [1..9] - write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes()); + ix_data.write_bytes(&self.amount.to_le_bytes()); let instruction = Instruction { program_id: &crate::ID, accounts: &account_metas, - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 9) }, + data: ix_data.read_bytes(), }; invoke_signed( diff --git a/programs/token/src/instructions/mint_to_checked.rs b/programs/token/src/instructions/mint_to_checked.rs index 32e30b6..031933a 100644 --- a/programs/token/src/instructions/mint_to_checked.rs +++ b/programs/token/src/instructions/mint_to_checked.rs @@ -1,5 +1,3 @@ -use core::slice::from_raw_parts; - use pinocchio::{ account_info::AccountInfo, instruction::{AccountMeta, Instruction, Signer}, @@ -7,7 +5,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{write_bytes, UNINIT_BYTE}; +use crate::{IxData, UNINIT_BYTE}; /// Mints new tokens to an account. /// @@ -50,16 +48,16 @@ impl<'a> MintToChecked<'a> { let mut instruction_data = [UNINIT_BYTE; 10]; // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[14]); + ix_data.write_bytes(&[14]); // Set amount as u64 at offset [1..9] - write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes()); + ix_data.write_bytes(&self.amount.to_le_bytes()); // Set decimals as u8 at offset [9] - write_bytes(&mut instruction_data[9..], &[self.decimals]); + ix_data.write_bytes(&[self.decimals]); let instruction = Instruction { program_id: &crate::ID, accounts: &account_metas, - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 10) }, + data: ix_data.read_bytes(), }; invoke_signed( diff --git a/programs/token/src/instructions/set_authority.rs b/programs/token/src/instructions/set_authority.rs index 2ded29b..47c58da 100644 --- a/programs/token/src/instructions/set_authority.rs +++ b/programs/token/src/instructions/set_authority.rs @@ -1,5 +1,3 @@ -use core::slice::from_raw_parts; - use pinocchio::{ account_info::AccountInfo, instruction::{AccountMeta, Instruction, Signer}, @@ -8,7 +6,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{write_bytes, UNINIT_BYTE}; +use crate::{IxData, UNINIT_BYTE}; #[repr(u8)] #[derive(Clone, Copy)] @@ -59,21 +57,16 @@ impl<'a> SetAuthority<'a> { let mut instruction_data = [UNINIT_BYTE; 35]; // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[6]); + ix_data.write_bytes(&[6]); // Set authority_type as u8 at offset [1] - write_bytes(&mut instruction_data[1..2], &[self.authority_type as u8]); + ix_data.write_bytes(&[self.authority_type as u8]); // Set new_authority as [u8; 32] at offset [2..35] - if let Some(new_authority) = self.new_authority { - write_bytes(&mut instruction_data[2..3], &[1]); - write_bytes(&mut instruction_data[3..], new_authority); - } else { - write_bytes(&mut instruction_data[2..3], &[0]); - } + ix_data.write_optional_pubkey_bytes(self.new_authority); let instruction = Instruction { program_id: &crate::ID, accounts: &account_metas, - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 35) }, + data: ix_data.read_bytes(), }; invoke_signed(&instruction, &[self.account, self.authority], signers) diff --git a/programs/token/src/instructions/transfer.rs b/programs/token/src/instructions/transfer.rs index ad58ce1..17267e4 100644 --- a/programs/token/src/instructions/transfer.rs +++ b/programs/token/src/instructions/transfer.rs @@ -1,5 +1,3 @@ -use core::slice::from_raw_parts; - use pinocchio::{ account_info::AccountInfo, instruction::{AccountMeta, Instruction, Signer}, @@ -7,7 +5,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{write_bytes, UNINIT_BYTE}; +use crate::{IxData, UNINIT_BYTE}; /// Transfer Tokens from one Token Account to another. /// @@ -46,14 +44,14 @@ impl<'a> Transfer<'a> { let mut instruction_data = [UNINIT_BYTE; 9]; // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[3]); + ix_data.write_bytes(&[3]); // Set amount as u64 at offset [1..9] - write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes()); + ix_data.write_bytes(&self.amount.to_le_bytes()); let instruction = Instruction { program_id: &crate::ID, accounts: &account_metas, - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 9) }, + data: ix_data.read_bytes(), }; invoke_signed(&instruction, &[self.from, self.to, self.authority], signers) diff --git a/programs/token/src/instructions/transfer_checked.rs b/programs/token/src/instructions/transfer_checked.rs index c4ded10..3f7f351 100644 --- a/programs/token/src/instructions/transfer_checked.rs +++ b/programs/token/src/instructions/transfer_checked.rs @@ -1,5 +1,3 @@ -use core::slice::from_raw_parts; - use pinocchio::{ account_info::AccountInfo, instruction::{AccountMeta, Instruction, Signer}, @@ -7,7 +5,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{write_bytes, UNINIT_BYTE}; +use crate::{IxData, UNINIT_BYTE}; /// Transfer Tokens from one Token Account to another. /// @@ -53,16 +51,16 @@ impl<'a> TransferChecked<'a> { let mut instruction_data = [UNINIT_BYTE; 10]; // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[12]); + ix_data.write_bytes(&[12]); // Set amount as u64 at offset [1..9] - write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes()); + ix_data.write_bytes(&self.amount.to_le_bytes()); // Set decimals as u8 at offset [9] - write_bytes(&mut instruction_data[9..], &[self.decimals]); + ix_data.write_bytes(&[self.decimals]); let instruction = Instruction { program_id: &crate::ID, accounts: &account_metas, - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 10) }, + data: ix_data.read_bytes(), }; invoke_signed(&instruction, &[self.from, self.to, self.authority], signers) diff --git a/programs/token/src/lib.rs b/programs/token/src/lib.rs index 1a8ab22..19f6b55 100644 --- a/programs/token/src/lib.rs +++ b/programs/token/src/lib.rs @@ -7,6 +7,8 @@ pinocchio_pubkey::declare_id!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); use core::mem::MaybeUninit; +use pinocchio::pubkey::PUBKEY_BYTES; + const UNINIT_BYTE: MaybeUninit = MaybeUninit::::uninit(); #[inline(always)] @@ -15,3 +17,76 @@ fn write_bytes(destination: &mut [MaybeUninit], source: &[u8]) { d.write(*s); } } + +pub struct IxData<'i> { + pub bytes: &'i mut [MaybeUninit], + pub current_size: usize, + pub capacity: usize, +} + +impl<'i> IxData<'i> { + pub fn new(data: &'i mut [MaybeUninit]) -> Self { + let capacity = data.len(); + Self { + bytes: data, + current_size: 0, + capacity, + } + } + + pub fn write_bytes(&mut self, source: &[u8]) { + if self.current_size + source.len() > self.capacity { + return; + } + + write_bytes( + &mut self.bytes[self.current_size..self.current_size + source.len()], + source, + ); + self.current_size += source.len(); + } + + pub fn write_optional_pubkey_bytes(&mut self, source: Option<&[u8; PUBKEY_BYTES]>) { + if let Some(source) = source { + self.write_bytes(&[1]); + self.write_bytes(source); + } else { + self.write_bytes(&[0]); + } + } + + pub fn read_bytes(&self) -> &[u8] { + unsafe { core::slice::from_raw_parts(self.bytes.as_ptr() as *const u8, self.current_size) } + } +} + +#[cfg(test)] +mod tests { + + #[test] + fn test_write_bytes() { + let mut data = [crate::UNINIT_BYTE; 50]; + let mut ix_data = crate::IxData::new(&mut data); + assert_eq!(ix_data.current_size, 0); + assert_eq!(ix_data.capacity, 50); + ix_data.write_bytes(&[1, 2, 3, 4]); + + let optional_pubkey = Some(&[2; 32]); + + ix_data.write_optional_pubkey_bytes(optional_pubkey); + + assert!(ix_data.current_size == 37); + + ix_data.write_bytes(&[5, 6, 7, 8, 9, 10]); + + assert!(ix_data.current_size == 43); + // assert_eq!(ix_data.read_bytes(), &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + assert_eq!( + ix_data.read_bytes(), + &[ + 1, 2, 3, 4, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 6, 7, 8, 9, 10 + ] + ); + } +} From a59103cbe4d86d2a0d9481dc09d55a679905e203 Mon Sep 17 00:00:00 2001 From: NagaprasadVr Date: Tue, 12 Nov 2024 10:23:40 +0530 Subject: [PATCH 02/13] chore:cleanup --- programs/token/src/instructions/approve.rs | 1 - .../src/instructions/initialize_account_2.rs | 1 - programs/token/src/lib.rs | 24 +++++++++---------- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/programs/token/src/instructions/approve.rs b/programs/token/src/instructions/approve.rs index c0c3e02..96278f5 100644 --- a/programs/token/src/instructions/approve.rs +++ b/programs/token/src/instructions/approve.rs @@ -47,7 +47,6 @@ impl<'a> Approve<'a> { // Set discriminator as u8 at offset [0] ix_data.write_bytes(&[4]); // Set amount as u64 at offset [1..9] - // write_bytes(&mut instruction_data[1..], &self.amount.to_le_bytes()); ix_data.write_bytes(&self.amount.to_le_bytes()); let instruction = Instruction { diff --git a/programs/token/src/instructions/initialize_account_2.rs b/programs/token/src/instructions/initialize_account_2.rs index 5c1b529..37374ad 100644 --- a/programs/token/src/instructions/initialize_account_2.rs +++ b/programs/token/src/instructions/initialize_account_2.rs @@ -46,7 +46,6 @@ impl<'a> InitilizeAccount2<'a> { // Set discriminator as u8 at offset [0] ix_data.write_bytes(&[16]); - // Set owner as [u8; 32] at offset [1..33] ix_data.write_bytes(self.owner.as_ref()); diff --git a/programs/token/src/lib.rs b/programs/token/src/lib.rs index 19f6b55..bd87900 100644 --- a/programs/token/src/lib.rs +++ b/programs/token/src/lib.rs @@ -11,13 +11,6 @@ use pinocchio::pubkey::PUBKEY_BYTES; const UNINIT_BYTE: MaybeUninit = MaybeUninit::::uninit(); -#[inline(always)] -fn write_bytes(destination: &mut [MaybeUninit], source: &[u8]) { - for (d, s) in destination.iter_mut().zip(source.iter()) { - d.write(*s); - } -} - pub struct IxData<'i> { pub bytes: &'i mut [MaybeUninit], pub current_size: usize, @@ -25,6 +18,7 @@ pub struct IxData<'i> { } impl<'i> IxData<'i> { + #[inline(always)] pub fn new(data: &'i mut [MaybeUninit]) -> Self { let capacity = data.len(); Self { @@ -34,18 +28,23 @@ impl<'i> IxData<'i> { } } + #[inline(always)] pub fn write_bytes(&mut self, source: &[u8]) { if self.current_size + source.len() > self.capacity { return; } - write_bytes( - &mut self.bytes[self.current_size..self.current_size + source.len()], - source, - ); + let start = self.current_size; + let end = start + source.len(); + + for (d, s) in &mut self.bytes[start..end].iter_mut().zip(source.iter()) { + d.write(*s); + } + self.current_size += source.len(); } + #[inline(always)] pub fn write_optional_pubkey_bytes(&mut self, source: Option<&[u8; PUBKEY_BYTES]>) { if let Some(source) = source { self.write_bytes(&[1]); @@ -55,6 +54,7 @@ impl<'i> IxData<'i> { } } + #[inline(always)] pub fn read_bytes(&self) -> &[u8] { unsafe { core::slice::from_raw_parts(self.bytes.as_ptr() as *const u8, self.current_size) } } @@ -80,7 +80,7 @@ mod tests { ix_data.write_bytes(&[5, 6, 7, 8, 9, 10]); assert!(ix_data.current_size == 43); - // assert_eq!(ix_data.read_bytes(), &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + assert_eq!( ix_data.read_bytes(), &[ From a090ad3f4278e30c0b99613e96b81f27f540a9c6 Mon Sep 17 00:00:00 2001 From: NagaprasadVr Date: Wed, 13 Nov 2024 08:26:10 +0530 Subject: [PATCH 03/13] add generic write_optional_bytes --- .../token/src/instructions/initialize_mint.rs | 4 ++-- .../src/instructions/initialize_mint_2.rs | 4 ++-- .../token/src/instructions/set_authority.rs | 4 ++-- programs/token/src/lib.rs | 22 ++++++++++++++++--- 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/programs/token/src/instructions/initialize_mint.rs b/programs/token/src/instructions/initialize_mint.rs index 8976bbe..da140a5 100644 --- a/programs/token/src/instructions/initialize_mint.rs +++ b/programs/token/src/instructions/initialize_mint.rs @@ -6,7 +6,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{IxData, UNINIT_BYTE}; +use crate::{FromOptPubkeyToOptBytes, IxData, UNINIT_BYTE}; /// Initialize a new mint. /// @@ -54,7 +54,7 @@ impl<'a> InitilizeMint<'a> { // Set mint_authority as Pubkey at offset [2..34] ix_data.write_bytes(self.mint_authority.as_ref()); // Set COption & freeze_authority at offset [34..67] - ix_data.write_optional_pubkey_bytes(self.freeze_authority); + ix_data.write_optional_bytes(self.freeze_authority.to_opt_slice()); let instruction = Instruction { program_id: &crate::ID, diff --git a/programs/token/src/instructions/initialize_mint_2.rs b/programs/token/src/instructions/initialize_mint_2.rs index 24487e3..d3bd898 100644 --- a/programs/token/src/instructions/initialize_mint_2.rs +++ b/programs/token/src/instructions/initialize_mint_2.rs @@ -6,7 +6,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{IxData, UNINIT_BYTE}; +use crate::{FromOptPubkeyToOptBytes, IxData, UNINIT_BYTE}; /// Initialize a new mint. /// @@ -48,7 +48,7 @@ impl<'a> InitilizeMint2<'a> { // Set mint_authority as Pubkey at offset [2..34] ix_data.write_bytes(self.mint_authority.as_ref()); // Set COption & freeze_authority at offset [34..67] - ix_data.write_optional_pubkey_bytes(self.freeze_authority); + ix_data.write_optional_bytes(self.freeze_authority.to_opt_slice()); let instruction = Instruction { program_id: &crate::ID, diff --git a/programs/token/src/instructions/set_authority.rs b/programs/token/src/instructions/set_authority.rs index 47c58da..21ba214 100644 --- a/programs/token/src/instructions/set_authority.rs +++ b/programs/token/src/instructions/set_authority.rs @@ -6,7 +6,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{IxData, UNINIT_BYTE}; +use crate::{FromOptPubkeyToOptBytes, IxData, UNINIT_BYTE}; #[repr(u8)] #[derive(Clone, Copy)] @@ -61,7 +61,7 @@ impl<'a> SetAuthority<'a> { // Set authority_type as u8 at offset [1] ix_data.write_bytes(&[self.authority_type as u8]); // Set new_authority as [u8; 32] at offset [2..35] - ix_data.write_optional_pubkey_bytes(self.new_authority); + ix_data.write_optional_bytes(self.new_authority.to_opt_slice()); let instruction = Instruction { program_id: &crate::ID, diff --git a/programs/token/src/lib.rs b/programs/token/src/lib.rs index bd87900..534b58f 100644 --- a/programs/token/src/lib.rs +++ b/programs/token/src/lib.rs @@ -11,6 +11,17 @@ use pinocchio::pubkey::PUBKEY_BYTES; const UNINIT_BYTE: MaybeUninit = MaybeUninit::::uninit(); +pub trait FromOptPubkeyToOptBytes { + fn to_opt_slice(&self) -> Option<&[u8]>; +} + +impl FromOptPubkeyToOptBytes for Option<&[u8; PUBKEY_BYTES]> { + #[inline(always)] + fn to_opt_slice(&self) -> Option<&[u8]> { + self.map(|pubkey| pubkey.as_ref()) + } +} + pub struct IxData<'i> { pub bytes: &'i mut [MaybeUninit], pub current_size: usize, @@ -45,7 +56,7 @@ impl<'i> IxData<'i> { } #[inline(always)] - pub fn write_optional_pubkey_bytes(&mut self, source: Option<&[u8; PUBKEY_BYTES]>) { + pub fn write_optional_bytes(&mut self, source: Option<&[u8]>) { if let Some(source) = source { self.write_bytes(&[1]); self.write_bytes(source); @@ -62,18 +73,23 @@ impl<'i> IxData<'i> { #[cfg(test)] mod tests { + use crate::FromOptPubkeyToOptBytes; #[test] fn test_write_bytes() { let mut data = [crate::UNINIT_BYTE; 50]; + let mut ix_data = crate::IxData::new(&mut data); + assert_eq!(ix_data.current_size, 0); + assert_eq!(ix_data.capacity, 50); + ix_data.write_bytes(&[1, 2, 3, 4]); - let optional_pubkey = Some(&[2; 32]); + let optional_pubkey: Option<&[u8; 32]> = Some(&[2; 32]); - ix_data.write_optional_pubkey_bytes(optional_pubkey); + ix_data.write_optional_bytes(optional_pubkey.to_opt_slice()); assert!(ix_data.current_size == 37); From 92c37f88e6683ad277565132b2f1ef2378c03f77 Mon Sep 17 00:00:00 2001 From: NagaprasadVr Date: Mon, 11 Nov 2024 09:00:23 +0530 Subject: [PATCH 04/13] WIP:add token extensions --- Cargo.lock | 8 + Cargo.toml | 1 + programs/token2022/Cargo.toml | 12 ++ programs/token2022/README.md | 42 ++++ programs/token2022/src/extensions/mod.rs | 1 + .../token2022/src/extensions/transfer_fee.rs | 103 +++++++++ .../token2022/src/instructions/approve.rs | 65 ++++++ .../src/instructions/approve_checked.rs | 74 +++++++ programs/token2022/src/instructions/burn.rs | 65 ++++++ .../src/instructions/burn_checked.rs | 69 ++++++ .../src/instructions/close_account.rs | 49 +++++ .../src/instructions/freeze_account.rs | 49 +++++ .../src/instructions/initialize_account.rs | 53 +++++ .../src/instructions/initialize_account_2.rs | 66 ++++++ .../src/instructions/initialize_account_3.rs | 58 +++++ .../src/instructions/initialize_mint.rs | 74 +++++++ .../src/instructions/initialize_mint_2.rs | 68 ++++++ .../token2022/src/instructions/mint_to.rs | 66 ++++++ .../src/instructions/mint_to_checked.rs | 71 +++++++ programs/token2022/src/instructions/mod.rs | 39 ++++ programs/token2022/src/instructions/revoke.rs | 41 ++++ .../src/instructions/set_authority.rs | 80 +++++++ .../token2022/src/instructions/sync_native.rs | 37 ++++ .../src/instructions/thaw_account.rs | 49 +++++ .../token2022/src/instructions/transfer.rs | 61 ++++++ .../src/instructions/transfer_checked.rs | 70 +++++++ programs/token2022/src/lib.rs | 23 ++ programs/token2022/src/state/account_state.rs | 36 ++++ programs/token2022/src/state/mint.rs | 145 +++++++++++++ programs/token2022/src/state/mod.rs | 8 + programs/token2022/src/state/token.rs | 198 ++++++++++++++++++ 31 files changed, 1781 insertions(+) create mode 100644 programs/token2022/Cargo.toml create mode 100644 programs/token2022/README.md create mode 100644 programs/token2022/src/extensions/mod.rs create mode 100644 programs/token2022/src/extensions/transfer_fee.rs create mode 100644 programs/token2022/src/instructions/approve.rs create mode 100644 programs/token2022/src/instructions/approve_checked.rs create mode 100644 programs/token2022/src/instructions/burn.rs create mode 100644 programs/token2022/src/instructions/burn_checked.rs create mode 100644 programs/token2022/src/instructions/close_account.rs create mode 100644 programs/token2022/src/instructions/freeze_account.rs create mode 100644 programs/token2022/src/instructions/initialize_account.rs create mode 100644 programs/token2022/src/instructions/initialize_account_2.rs create mode 100644 programs/token2022/src/instructions/initialize_account_3.rs create mode 100644 programs/token2022/src/instructions/initialize_mint.rs create mode 100644 programs/token2022/src/instructions/initialize_mint_2.rs create mode 100644 programs/token2022/src/instructions/mint_to.rs create mode 100644 programs/token2022/src/instructions/mint_to_checked.rs create mode 100644 programs/token2022/src/instructions/mod.rs create mode 100644 programs/token2022/src/instructions/revoke.rs create mode 100644 programs/token2022/src/instructions/set_authority.rs create mode 100644 programs/token2022/src/instructions/sync_native.rs create mode 100644 programs/token2022/src/instructions/thaw_account.rs create mode 100644 programs/token2022/src/instructions/transfer.rs create mode 100644 programs/token2022/src/instructions/transfer_checked.rs create mode 100644 programs/token2022/src/lib.rs create mode 100644 programs/token2022/src/state/account_state.rs create mode 100644 programs/token2022/src/state/mint.rs create mode 100644 programs/token2022/src/state/mod.rs create mode 100644 programs/token2022/src/state/token.rs diff --git a/Cargo.lock b/Cargo.lock index ffa7aa3..a4515ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -60,6 +60,14 @@ dependencies = [ "pinocchio-pubkey", ] +[[package]] +name = "pinocchio-token2022" +version = "0.2.0" +dependencies = [ + "pinocchio", + "pinocchio-pubkey", +] + [[package]] name = "proc-macro2" version = "1.0.89" diff --git a/Cargo.toml b/Cargo.toml index 9709685..bcd4761 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ resolver = "2" members = [ "programs/system", "programs/token", + "programs/token2022", "sdk/log/crate", "sdk/log/macro", "sdk/pinocchio", diff --git a/programs/token2022/Cargo.toml b/programs/token2022/Cargo.toml new file mode 100644 index 0000000..28b6666 --- /dev/null +++ b/programs/token2022/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "pinocchio-token2022" +description = "Pinocchio helpers to invoke Token program 2022 instructions" +version = "0.2.0" +edition = { workspace = true } +license = { workspace = true } +readme = "./README.md" +repository = { workspace = true } + +[dependencies] +pinocchio = { workspace = true } +pinocchio-pubkey = { workspace = true } diff --git a/programs/token2022/README.md b/programs/token2022/README.md new file mode 100644 index 0000000..9bfaa09 --- /dev/null +++ b/programs/token2022/README.md @@ -0,0 +1,42 @@ +# pinocchio-token2022 + +This crate contains [`pinocchio`](https://crates.io/crates/pinocchio) helpers to perform cross-program invocations (CPIs) for Token 2022 instructions. + +Each instruction defines an `struct` with the accounts and parameters required. Once all values are set, you can call directly `invoke` or `invoke_signed` to perform the CPI. + +This is a `no_std` crate. + +> **Note:** The API defined in this crate is subject to change. + +## Examples + +Initializing a mint account: + +```rust +// This example assumes that the instruction receives a writable `mint` +// account; `authority` is a `Pubkey`. +InitilizeMint { + mint, + rent_sysvar, + decimals: 9, + mint_authority: authority, + freeze_authority: Some(authority), +}.invoke()?; +``` + +Performing a transfer of tokens: + +```rust +// This example assumes that the instruction receives writable `from` and `to` +// accounts, and a signer `authority` account. +Transfer { + from, + to, + authority, + amount: 10, +}.invoke()?; +``` + +## License + +The code is licensed under the [Apache License Version 2.0](../LICENSE) diff --git a/programs/token2022/src/extensions/mod.rs b/programs/token2022/src/extensions/mod.rs new file mode 100644 index 0000000..c245c84 --- /dev/null +++ b/programs/token2022/src/extensions/mod.rs @@ -0,0 +1 @@ +pub mod transfer_fee; diff --git a/programs/token2022/src/extensions/transfer_fee.rs b/programs/token2022/src/extensions/transfer_fee.rs new file mode 100644 index 0000000..ec4a6e7 --- /dev/null +++ b/programs/token2022/src/extensions/transfer_fee.rs @@ -0,0 +1,103 @@ +use core::slice::from_raw_parts; + +use pinocchio::{ + account_info::AccountInfo, + instruction::{AccountMeta, Instruction, Signer}, + program::invoke_signed, + pubkey::Pubkey, + ProgramResult, +}; + +use crate::{write_bytes, UNINIT_BYTE}; + +/// State + +#[repr(C)] +pub struct TransferFee { + /// First epoch where the transfer fee takes effect + pub epoch: [u8; 8], + /// Maximum fee assessed on transfers, expressed as an amount of tokens + pub maximum_fee: [u8; 8], + /// Amount of transfer collected as fees, expressed as basis points of the + /// transfer amount, ie. increments of 0.01% + pub transfer_fee_basis_points: [u8; 8], +} + +#[repr(C)] +pub struct TransferFeeConfig { + /// flag to indicate if the transfer fee config authority is present + pub transfer_fee_config_authority_flag: [u8; 4], + /// Optional authority to set the fee + pub transfer_fee_config_authority: Pubkey, + /// flag to indicate if the withdraw authority is present + pub withdraw_withheld_authority_flag: [u8; 4], + /// Withdraw from mint instructions must be signed by this key + pub withdraw_withheld_authority: Pubkey, + /// Withheld transfer fee tokens that have been moved to the mint for + /// withdrawal + pub withheld_amount: [u8; 8], + /// Older transfer fee, used if the current epoch < new_transfer_fee.epoch + pub older_transfer_fee: TransferFee, + /// Newer transfer fee, used if the current epoch >= new_transfer_fee.epoch + pub newer_transfer_fee: TransferFee, +} + +/// Instruction + +pub struct InitializeTransferFeeConfig<'a> { + // Mint account + pub mint: &'a AccountInfo, + /// Pubkey that may update the fees + pub transfer_fee_config_authority: Option, + /// Withdraw instructions must be signed by this key + pub withdraw_withheld_authority: Option, + /// Amount of transfer collected as fees, expressed as basis points of + /// the transfer amount + pub transfer_fee_basis_points: u16, + /// Maximum fee assessed on transfers + pub maximum_fee: u64, +} + +impl<'a> InitializeTransferFeeConfig<'a> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + // Instruction data layout: + // - [0]: instruction discriminator + // - [1..33]: mint + // - [33]: transfer_fee_config_authority_flag + // - [34..66]: transfer_fee_config_authority + // - [66]: withdraw_withheld_authority_flag + // - [67..99]: withdraw_withheld_authority + // - [99..101]: transfer_fee_basis_points + // - [101..109]: maximum_fee + + let mut instruction_data = [UNINIT_BYTE; 83]; + + // Set discriminator as u8 at offset [0] + write_bytes(&mut instruction_data, &[todo!()]); + // Set mint as Pubkey at offset [1..33] + write_bytes(&mut instruction_data[1..33], self.mint.key().as_ref()); + // Set transfer_fee_config_authority COption at offset [33..37] + if let Some(transfer_fee_config_authority) = self.transfer_fee_config_authority { + write_bytes(&mut instruction_data[33..37], &[1]); + write_bytes( + &mut instruction_data[37..69], + transfer_fee_config_authority.as_ref(), + ); + } else { + write_bytes(&mut instruction_data[33..37], &[0]); + } + + let instruction = Instruction { + program_id: &crate::ID, + accounts: &[AccountMeta::writable(self.mint.key())], + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 9) }, + }; + + invoke_signed(&instruction, &[self.mint], signers) + } +} diff --git a/programs/token2022/src/instructions/approve.rs b/programs/token2022/src/instructions/approve.rs new file mode 100644 index 0000000..d9596f7 --- /dev/null +++ b/programs/token2022/src/instructions/approve.rs @@ -0,0 +1,65 @@ +use core::slice::from_raw_parts; + +use pinocchio::{ + account_info::AccountInfo, + instruction::{AccountMeta, Instruction, Signer}, + program::invoke_signed, + ProgramResult, +}; + +use crate::{write_bytes, UNINIT_BYTE}; + +/// Approves a delegate. +/// +/// ### Accounts: +/// 0. `[WRITE]` The token account. +/// 1. `[]` The delegate. +/// 2. `[SIGNER]` The source account owner. +pub struct Approve<'a> { + /// Source Account. + pub token: &'a AccountInfo, + /// Delegate Account + pub delegate: &'a AccountInfo, + /// Source Owner Account + pub authority: &'a AccountInfo, + /// Amount + pub amount: u64, +} + +impl<'a> Approve<'a> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + // Account metadata + let account_metas: [AccountMeta; 3] = [ + AccountMeta::writable(self.token.key()), + AccountMeta::readonly(self.delegate.key()), + AccountMeta::readonly_signer(self.authority.key()), + ]; + + // Instruction data + // - [0]: instruction discriminator (1 byte, u8) + // - [1..9]: amount (8 bytes, u64) + let mut instruction_data = [UNINIT_BYTE; 9]; + + // Set discriminator as u8 at offset [0] + write_bytes(&mut instruction_data, &[4]); + // Set amount as u64 at offset [1..9] + write_bytes(&mut instruction_data[1..], &self.amount.to_le_bytes()); + + let instruction = Instruction { + program_id: &crate::ID, + accounts: &account_metas, + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 9) }, + }; + + invoke_signed( + &instruction, + &[self.token, self.delegate, self.authority], + signers, + ) + } +} diff --git a/programs/token2022/src/instructions/approve_checked.rs b/programs/token2022/src/instructions/approve_checked.rs new file mode 100644 index 0000000..6195122 --- /dev/null +++ b/programs/token2022/src/instructions/approve_checked.rs @@ -0,0 +1,74 @@ +use core::slice::from_raw_parts; + +use pinocchio::{ + account_info::AccountInfo, + instruction::{AccountMeta, Instruction, Signer}, + program::invoke_signed, + ProgramResult, +}; + +use crate::{write_bytes, UNINIT_BYTE}; + +/// Approves a delegate. +/// +/// ### Accounts: +/// 0. `[WRITE]` The source account. +/// 1. `[]` The token mint. +/// 2. `[]` The delegate. +/// 3. `[SIGNER]` The source account owner. +pub struct ApproveChecked<'a> { + /// Source Account. + pub token: &'a AccountInfo, + /// Mint Account. + pub mint: &'a AccountInfo, + /// Delegate Account. + pub delegate: &'a AccountInfo, + /// Source Owner Account. + pub authority: &'a AccountInfo, + /// Amount. + pub amount: u64, + /// Decimals. + pub decimals: u8, +} + +impl<'a> ApproveChecked<'a> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + // Account metadata + let account_metas: [AccountMeta; 4] = [ + AccountMeta::writable(self.token.key()), + AccountMeta::readonly(self.mint.key()), + AccountMeta::readonly(self.delegate.key()), + AccountMeta::readonly_signer(self.authority.key()), + ]; + + // Instruction data + // - [0] : instruction discriminator (1 byte, u8) + // - [1..9]: amount (8 bytes, u64) + // - [9] : decimals (1 byte, u8) + let mut instruction_data = [UNINIT_BYTE; 10]; + + // Set discriminator as u8 at offset [0] + write_bytes(&mut instruction_data, &[13]); + // Set amount as u64 at offset [1..9] + write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes()); + // Set decimals as u8 at offset [9] + write_bytes(&mut instruction_data[9..], &[self.decimals]); + + let instruction = Instruction { + program_id: &crate::ID, + accounts: &account_metas, + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 10) }, + }; + + invoke_signed( + &instruction, + &[self.token, self.mint, self.delegate, self.authority], + signers, + ) + } +} diff --git a/programs/token2022/src/instructions/burn.rs b/programs/token2022/src/instructions/burn.rs new file mode 100644 index 0000000..4a8e71a --- /dev/null +++ b/programs/token2022/src/instructions/burn.rs @@ -0,0 +1,65 @@ +use core::slice::from_raw_parts; + +use pinocchio::{ + account_info::AccountInfo, + instruction::{AccountMeta, Instruction, Signer}, + program::invoke_signed, + ProgramResult, +}; + +use crate::{write_bytes, UNINIT_BYTE}; + +/// Burns tokens by removing them from an account. +/// +/// ### Accounts: +/// 0. `[WRITE]` The account to burn from. +/// 1. `[WRITE]` The token mint. +/// 2. `[SIGNER]` The account's owner/delegate. +pub struct Burn<'a> { + /// Source of the Burn Account + pub token: &'a AccountInfo, + /// Mint Account + pub mint: &'a AccountInfo, + /// Owner of the Token Account + pub authority: &'a AccountInfo, + /// Amount + pub amount: u64, +} + +impl<'a> Burn<'a> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + // Account metadata + let account_metas: [AccountMeta; 3] = [ + AccountMeta::writable(self.token.key()), + AccountMeta::writable(self.mint.key()), + AccountMeta::readonly_signer(self.authority.key()), + ]; + + // Instruction data + // - [0]: instruction discriminator (1 byte, u8) + // - [1..9]: amount (8 bytes, u64) + let mut instruction_data = [UNINIT_BYTE; 9]; + + // Set discriminator as u8 at offset [0] + write_bytes(&mut instruction_data, &[8]); + // Set amount as u64 at offset [1..9] + write_bytes(&mut instruction_data[1..], &self.amount.to_le_bytes()); + + let instruction = Instruction { + program_id: &crate::ID, + accounts: &account_metas, + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 9) }, + }; + + invoke_signed( + &instruction, + &[self.token, self.mint, self.authority], + signers, + ) + } +} diff --git a/programs/token2022/src/instructions/burn_checked.rs b/programs/token2022/src/instructions/burn_checked.rs new file mode 100644 index 0000000..789a320 --- /dev/null +++ b/programs/token2022/src/instructions/burn_checked.rs @@ -0,0 +1,69 @@ +use core::slice::from_raw_parts; + +use crate::{write_bytes, UNINIT_BYTE}; +use pinocchio::{ + account_info::AccountInfo, + instruction::{AccountMeta, Instruction, Signer}, + program::invoke_signed, + ProgramResult, +}; + +/// Burns tokens by removing them from an account. +/// +/// ### Accounts: +/// 0. `[WRITE]` The account to burn from. +/// 1. `[WRITE]` The token mint. +/// 2. `[SIGNER]` The account's owner/delegate. +pub struct BurnChecked<'a> { + /// Source of the Burn Account + pub token: &'a AccountInfo, + /// Mint Account + pub mint: &'a AccountInfo, + /// Owner of the Token Account + pub authority: &'a AccountInfo, + /// Amount + pub amount: u64, + /// Decimals + pub decimals: u8, +} + +impl<'a> BurnChecked<'a> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + // Account metadata + let account_metas: [AccountMeta; 3] = [ + AccountMeta::writable(self.token.key()), + AccountMeta::writable(self.mint.key()), + AccountMeta::readonly_signer(self.authority.key()), + ]; + + // Instruction data + // - [0]: instruction discriminator (1 byte, u8) + // - [1..9]: amount (8 bytes, u64) + // - [9]: decimals (1 byte, u8) + let mut instruction_data = [UNINIT_BYTE; 10]; + + // Set discriminator as u8 at offset [0] + write_bytes(&mut instruction_data, &[15]); + // Set amount as u64 at offset [1..9] + write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes()); + // Set decimals as u8 at offset [9] + write_bytes(&mut instruction_data[9..], &[self.decimals]); + + let instruction = Instruction { + program_id: &crate::ID, + accounts: &account_metas, + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 10) }, + }; + + invoke_signed( + &instruction, + &[self.token, self.mint, self.authority], + signers, + ) + } +} diff --git a/programs/token2022/src/instructions/close_account.rs b/programs/token2022/src/instructions/close_account.rs new file mode 100644 index 0000000..19461d9 --- /dev/null +++ b/programs/token2022/src/instructions/close_account.rs @@ -0,0 +1,49 @@ +use pinocchio::{ + account_info::AccountInfo, + instruction::{AccountMeta, Instruction, Signer}, + program::invoke_signed, + ProgramResult, +}; + +/// Close an account by transferring all its SOL to the destination account. +/// +/// ### Accounts: +/// 0. `[WRITE]` The account to close. +/// 1. `[WRITE]` The destination account. +/// 2. `[SIGNER]` The account's owner. +pub struct CloseAccount<'a> { + /// Token Account. + pub account: &'a AccountInfo, + /// Destination Account + pub destination: &'a AccountInfo, + /// Owner Account + pub authority: &'a AccountInfo, +} + +impl<'a> CloseAccount<'a> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + // account metadata + let account_metas: [AccountMeta; 3] = [ + AccountMeta::writable(self.account.key()), + AccountMeta::writable(self.destination.key()), + AccountMeta::readonly_signer(self.authority.key()), + ]; + + let instruction = Instruction { + program_id: &crate::ID, + accounts: &account_metas, + data: &[9], + }; + + invoke_signed( + &instruction, + &[self.account, self.destination, self.authority], + signers, + ) + } +} diff --git a/programs/token2022/src/instructions/freeze_account.rs b/programs/token2022/src/instructions/freeze_account.rs new file mode 100644 index 0000000..a2c9c02 --- /dev/null +++ b/programs/token2022/src/instructions/freeze_account.rs @@ -0,0 +1,49 @@ +use pinocchio::{ + account_info::AccountInfo, + instruction::{AccountMeta, Instruction, Signer}, + program::invoke_signed, + ProgramResult, +}; + +/// Freeze an Initialized account using the Mint's freeze_authority +/// +/// ### Accounts: +/// 0. `[WRITE]` The account to freeze. +/// 1. `[]` The token mint. +/// 2. `[SIGNER]` The mint freeze authority. +pub struct FreezeAccount<'a> { + /// Token Account to freeze. + pub token: &'a AccountInfo, + /// Mint Account. + pub mint: &'a AccountInfo, + /// Mint Freeze Authority Account + pub freeze_authority: &'a AccountInfo, +} + +impl<'a> FreezeAccount<'a> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + // account metadata + let account_metas: [AccountMeta; 3] = [ + AccountMeta::writable(self.token.key()), + AccountMeta::readonly(self.mint.key()), + AccountMeta::readonly_signer(self.freeze_authority.key()), + ]; + + let instruction = Instruction { + program_id: &crate::ID, + accounts: &account_metas, + data: &[10], + }; + + invoke_signed( + &instruction, + &[self.token, self.mint, self.freeze_authority], + signers, + ) + } +} diff --git a/programs/token2022/src/instructions/initialize_account.rs b/programs/token2022/src/instructions/initialize_account.rs new file mode 100644 index 0000000..5d5603d --- /dev/null +++ b/programs/token2022/src/instructions/initialize_account.rs @@ -0,0 +1,53 @@ +use pinocchio::{ + account_info::AccountInfo, + instruction::{AccountMeta, Instruction, Signer}, + program::invoke_signed, + ProgramResult, +}; + +/// Initialize a new Token Account. +/// +/// ### Accounts: +/// 0. `[WRITE]` The account to initialize. +/// 1. `[]` The mint this account will be associated with. +/// 2. `[]` The new account's owner/multisignature. +/// 3. `[]` Rent sysvar +pub struct InitilizeAccount<'a> { + /// New Account. + pub token: &'a AccountInfo, + /// Mint Account. + pub mint: &'a AccountInfo, + /// Owner of the new Account. + pub owner: &'a AccountInfo, + /// Rent Sysvar Account + pub rent_sysvar: &'a AccountInfo, +} + +impl<'a> InitilizeAccount<'a> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + // account metadata + let account_metas: [AccountMeta; 4] = [ + AccountMeta::writable(self.token.key()), + AccountMeta::readonly(self.mint.key()), + AccountMeta::readonly(self.owner.key()), + AccountMeta::readonly(self.rent_sysvar.key()), + ]; + + let instruction = Instruction { + program_id: &crate::ID, + accounts: &account_metas, + data: &[1], + }; + + invoke_signed( + &instruction, + &[self.token, self.mint, self.owner, self.rent_sysvar], + signers, + ) + } +} diff --git a/programs/token2022/src/instructions/initialize_account_2.rs b/programs/token2022/src/instructions/initialize_account_2.rs new file mode 100644 index 0000000..430d9e9 --- /dev/null +++ b/programs/token2022/src/instructions/initialize_account_2.rs @@ -0,0 +1,66 @@ +use core::slice::from_raw_parts; + +use pinocchio::{ + account_info::AccountInfo, + instruction::{AccountMeta, Instruction, Signer}, + program::invoke_signed, + pubkey::Pubkey, + ProgramResult, +}; + +use crate::{write_bytes, UNINIT_BYTE}; + +/// Initialize a new Token Account. +/// +/// ### Accounts: +/// 0. `[WRITE]` The account to initialize. +/// 1. `[]` The mint this account will be associated with. +/// 3. `[]` Rent sysvar +pub struct InitilizeAccount2<'a> { + /// New Account. + pub token: &'a AccountInfo, + /// Mint Account. + pub mint: &'a AccountInfo, + /// Rent Sysvar Account + pub rent_sysvar: &'a AccountInfo, + /// Owner of the new Account. + pub owner: &'a Pubkey, +} + +impl<'a> InitilizeAccount2<'a> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + // account metadata + let account_metas: [AccountMeta; 3] = [ + AccountMeta::writable(self.token.key()), + AccountMeta::readonly(self.mint.key()), + AccountMeta::readonly(self.rent_sysvar.key()), + ]; + + // instruction data + // - [0]: instruction discriminator + // - [1..33]: owner + let mut instruction_data = [UNINIT_BYTE; 33]; + + // Set discriminator as u8 at offset [0] + write_bytes(&mut instruction_data, &[16]); + // Set owner as [u8; 32] at offset [1..33] + write_bytes(&mut instruction_data[1..], self.owner); + + let instruction = Instruction { + program_id: &crate::ID, + accounts: &account_metas, + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 33) }, + }; + + invoke_signed( + &instruction, + &[self.token, self.mint, self.rent_sysvar], + signers, + ) + } +} diff --git a/programs/token2022/src/instructions/initialize_account_3.rs b/programs/token2022/src/instructions/initialize_account_3.rs new file mode 100644 index 0000000..6a573c5 --- /dev/null +++ b/programs/token2022/src/instructions/initialize_account_3.rs @@ -0,0 +1,58 @@ +use core::slice::from_raw_parts; + +use pinocchio::{ + account_info::AccountInfo, + instruction::{AccountMeta, Instruction, Signer}, + program::invoke_signed, + pubkey::Pubkey, + ProgramResult, +}; + +use crate::{write_bytes, UNINIT_BYTE}; + +/// Initialize a new Token Account. +/// +/// ### Accounts: +/// 0. `[WRITE]` The account to initialize. +/// 1. `[]` The mint this account will be associated with. +pub struct InitilizeAccount3<'a> { + /// New Account. + pub token: &'a AccountInfo, + /// Mint Account. + pub mint: &'a AccountInfo, + /// Owner of the new Account. + pub owner: &'a Pubkey, +} + +impl<'a> InitilizeAccount3<'a> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + // account metadata + let account_metas: [AccountMeta; 2] = [ + AccountMeta::writable(self.token.key()), + AccountMeta::readonly(self.mint.key()), + ]; + + // instruction data + // - [0]: instruction discriminator + // - [1..33]: owner + let mut instruction_data = [UNINIT_BYTE; 33]; + + // Set discriminator as u8 at offset [0] + write_bytes(&mut instruction_data, &[18]); + // Set owner as [u8; 32] at offset [1..33] + write_bytes(&mut instruction_data[1..], self.owner); + + let instruction = Instruction { + program_id: &crate::ID, + accounts: &account_metas, + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 33) }, + }; + + invoke_signed(&instruction, &[self.token, self.mint], signers) + } +} diff --git a/programs/token2022/src/instructions/initialize_mint.rs b/programs/token2022/src/instructions/initialize_mint.rs new file mode 100644 index 0000000..1e91c89 --- /dev/null +++ b/programs/token2022/src/instructions/initialize_mint.rs @@ -0,0 +1,74 @@ +use core::slice::from_raw_parts; + +use pinocchio::{ + account_info::AccountInfo, + instruction::{AccountMeta, Instruction, Signer}, + program::invoke_signed, + pubkey::Pubkey, + ProgramResult, +}; + +use crate::{write_bytes, UNINIT_BYTE}; + +/// Initialize a new mint. +/// +/// ### Accounts: +/// 0. `[WRITABLE]` Mint account +/// 1. `[]` Rent sysvar +pub struct InitilizeMint<'a> { + /// Mint Account. + pub mint: &'a AccountInfo, + /// Rent sysvar Account. + pub rent_sysvar: &'a AccountInfo, + /// Decimals. + pub decimals: u8, + /// Mint Authority. + pub mint_authority: &'a Pubkey, + /// Freeze Authority. + pub freeze_authority: Option<&'a Pubkey>, +} + +impl<'a> InitilizeMint<'a> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + // Account metadata + let account_metas: [AccountMeta; 2] = [ + AccountMeta::writable(self.mint.key()), + AccountMeta::readonly(self.rent_sysvar.key()), + ]; + + // Instruction data layout: + // - [0]: instruction discriminator + // - [1]: decimals + // - [2..34]: mint_authority + // - [34]: freeze_authority presence flag + // - [35..68]: freeze_authority + let mut instruction_data = [UNINIT_BYTE; 67]; + + // Set discriminator as u8 at offset [0] + write_bytes(&mut instruction_data, &[0]); + // Set decimals as u8 at offset [1] + write_bytes(&mut instruction_data[1..2], &[self.decimals]); + // Set mint_authority as Pubkey at offset [2..34] + write_bytes(&mut instruction_data[2..34], self.mint_authority); + // Set COption & freeze_authority at offset [34..67] + if let Some(freeze_auth) = self.freeze_authority { + write_bytes(&mut instruction_data[34..35], &[1]); + write_bytes(&mut instruction_data[35..], freeze_auth); + } else { + write_bytes(&mut instruction_data[34..35], &[0]); + } + + let instruction = Instruction { + program_id: &crate::ID, + accounts: &account_metas, + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 67) }, + }; + + invoke_signed(&instruction, &[self.mint, self.rent_sysvar], signers) + } +} diff --git a/programs/token2022/src/instructions/initialize_mint_2.rs b/programs/token2022/src/instructions/initialize_mint_2.rs new file mode 100644 index 0000000..6a53ca5 --- /dev/null +++ b/programs/token2022/src/instructions/initialize_mint_2.rs @@ -0,0 +1,68 @@ +use core::slice::from_raw_parts; + +use pinocchio::{ + account_info::AccountInfo, + instruction::{AccountMeta, Instruction, Signer}, + program::invoke_signed, + pubkey::Pubkey, + ProgramResult, +}; + +use crate::{write_bytes, UNINIT_BYTE}; + +/// Initialize a new mint. +/// +/// ### Accounts: +/// 0. `[WRITABLE]` Mint account +pub struct InitilizeMint2<'a> { + /// Mint Account. + pub mint: &'a AccountInfo, + /// Decimals. + pub decimals: u8, + /// Mint Authority. + pub mint_authority: &'a Pubkey, + /// Freeze Authority. + pub freeze_authority: Option<&'a Pubkey>, +} + +impl<'a> InitilizeMint2<'a> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + // Account metadata + let account_metas: [AccountMeta; 1] = [AccountMeta::writable(self.mint.key())]; + + // Instruction data layout: + // - [0]: instruction discriminator + // - [1]: decimals + // - [2..34]: mint_authority + // - [34..35]: freeze_authority presence flag + // - [35..67]: freeze_authority + let mut instruction_data = [UNINIT_BYTE; 67]; + + // Set discriminator as u8 at offset [0] + write_bytes(&mut instruction_data, &[20]); + // Set decimals as u8 at offset [1] + write_bytes(&mut instruction_data[1..2], &[self.decimals]); + // Set mint_authority as Pubkey at offset [2..34] + write_bytes(&mut instruction_data[2..34], self.mint_authority); + // Set COption & freeze_authority at offset [34..67] + if let Some(freeze_auth) = self.freeze_authority { + write_bytes(&mut instruction_data[34..35], &[1]); + write_bytes(&mut instruction_data[35..], freeze_auth); + } else { + write_bytes(&mut instruction_data[34..35], &[0]); + } + + let instruction = Instruction { + program_id: &crate::ID, + accounts: &account_metas, + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 67) }, + }; + + invoke_signed(&instruction, &[self.mint], signers) + } +} diff --git a/programs/token2022/src/instructions/mint_to.rs b/programs/token2022/src/instructions/mint_to.rs new file mode 100644 index 0000000..f0da1d4 --- /dev/null +++ b/programs/token2022/src/instructions/mint_to.rs @@ -0,0 +1,66 @@ +use core::slice::from_raw_parts; + +use pinocchio::{ + account_info::AccountInfo, + instruction::{AccountMeta, Instruction, Signer}, + program::invoke_signed, + ProgramResult, +}; + +use crate::{write_bytes, UNINIT_BYTE}; + +/// Mints new tokens to an account. +/// +/// ### Accounts: +/// 0. `[WRITE]` The mint. +/// 1. `[WRITE]` The account to mint tokens to. +/// 2. `[SIGNER]` The mint's minting authority. +/// +pub struct MintTo<'a> { + /// Mint Account. + pub mint: &'a AccountInfo, + /// Token Account. + pub token: &'a AccountInfo, + /// Mint Authority + pub mint_authority: &'a AccountInfo, + /// Amount + pub amount: u64, +} + +impl<'a> MintTo<'a> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + // account metadata + let account_metas: [AccountMeta; 3] = [ + AccountMeta::writable(self.mint.key()), + AccountMeta::writable(self.token.key()), + AccountMeta::readonly_signer(self.mint_authority.key()), + ]; + + // Instruction data layout: + // - [0]: instruction discriminator + // - [1..9]: amount + let mut instruction_data = [UNINIT_BYTE; 9]; + + // Set discriminator as u8 at offset [0] + write_bytes(&mut instruction_data, &[7]); + // Set amount as u64 at offset [1..9] + write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes()); + + let instruction = Instruction { + program_id: &crate::ID, + accounts: &account_metas, + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 9) }, + }; + + invoke_signed( + &instruction, + &[self.mint, self.token, self.mint_authority], + signers, + ) + } +} diff --git a/programs/token2022/src/instructions/mint_to_checked.rs b/programs/token2022/src/instructions/mint_to_checked.rs new file mode 100644 index 0000000..67d0608 --- /dev/null +++ b/programs/token2022/src/instructions/mint_to_checked.rs @@ -0,0 +1,71 @@ +use core::slice::from_raw_parts; + +use pinocchio::{ + account_info::AccountInfo, + instruction::{AccountMeta, Instruction, Signer}, + program::invoke_signed, + ProgramResult, +}; + +use crate::{write_bytes, UNINIT_BYTE}; + +/// Mints new tokens to an account. +/// +/// ### Accounts: +/// 0. `[WRITE]` The mint. +/// 1. `[WRITE]` The account to mint tokens to. +/// 2. `[SIGNER]` The mint's minting authority. +/// +pub struct MintToChecked<'a> { + /// Mint Account. + pub mint: &'a AccountInfo, + /// Token Account. + pub token: &'a AccountInfo, + /// Mint Authority + pub mint_authority: &'a AccountInfo, + /// Amount + pub amount: u64, + /// Decimals + pub decimals: u8, +} + +impl<'a> MintToChecked<'a> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + // account metadata + let account_metas: [AccountMeta; 3] = [ + AccountMeta::writable(self.mint.key()), + AccountMeta::writable(self.token.key()), + AccountMeta::readonly_signer(self.mint_authority.key()), + ]; + + // Instruction data layout: + // - [0]: instruction discriminator + // - [1..9]: amount + // - [9]: decimals + let mut instruction_data = [UNINIT_BYTE; 10]; + + // Set discriminator as u8 at offset [0] + write_bytes(&mut instruction_data, &[14]); + // Set amount as u64 at offset [1..9] + write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes()); + // Set decimals as u8 at offset [9] + write_bytes(&mut instruction_data[9..], &[self.decimals]); + + let instruction = Instruction { + program_id: &crate::ID, + accounts: &account_metas, + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 10) }, + }; + + invoke_signed( + &instruction, + &[self.mint, self.token, self.mint_authority], + signers, + ) + } +} diff --git a/programs/token2022/src/instructions/mod.rs b/programs/token2022/src/instructions/mod.rs new file mode 100644 index 0000000..9287f8f --- /dev/null +++ b/programs/token2022/src/instructions/mod.rs @@ -0,0 +1,39 @@ +mod approve; +mod approve_checked; +mod burn; +mod burn_checked; +mod close_account; +mod freeze_account; +mod initialize_account; +mod initialize_account_2; +mod initialize_account_3; +mod initialize_mint; +mod initialize_mint_2; +mod mint_to; +mod mint_to_checked; +mod revoke; +mod set_authority; +mod sync_native; +mod thaw_account; +mod transfer; +mod transfer_checked; + +pub use approve::*; +pub use approve_checked::*; +pub use burn::*; +pub use burn_checked::*; +pub use close_account::*; +pub use freeze_account::*; +pub use initialize_account::*; +pub use initialize_account_2::*; +pub use initialize_account_3::*; +pub use initialize_mint::*; +pub use initialize_mint_2::*; +pub use mint_to::*; +pub use mint_to_checked::*; +pub use revoke::*; +pub use set_authority::*; +pub use sync_native::*; +pub use thaw_account::*; +pub use transfer::*; +pub use transfer_checked::*; diff --git a/programs/token2022/src/instructions/revoke.rs b/programs/token2022/src/instructions/revoke.rs new file mode 100644 index 0000000..75a7f6b --- /dev/null +++ b/programs/token2022/src/instructions/revoke.rs @@ -0,0 +1,41 @@ +use pinocchio::{ + account_info::AccountInfo, + instruction::{AccountMeta, Instruction, Signer}, + program::invoke_signed, + ProgramResult, +}; + +/// Revokes the delegate's authority. +/// +/// ### Accounts: +/// 0. `[WRITE]` The source account. +/// 1. `[SIGNER]` The source account owner. +pub struct Revoke<'a> { + /// New Account. + pub token: &'a AccountInfo, + /// Mint Account. + pub authority: &'a AccountInfo, +} + +impl<'a> Revoke<'a> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + // account metadata + let account_metas: [AccountMeta; 2] = [ + AccountMeta::writable(self.token.key()), + AccountMeta::readonly_signer(self.authority.key()), + ]; + + let instruction = Instruction { + program_id: &crate::ID, + accounts: &account_metas, + data: &[5], + }; + + invoke_signed(&instruction, &[self.token, self.authority], signers) + } +} diff --git a/programs/token2022/src/instructions/set_authority.rs b/programs/token2022/src/instructions/set_authority.rs new file mode 100644 index 0000000..a658349 --- /dev/null +++ b/programs/token2022/src/instructions/set_authority.rs @@ -0,0 +1,80 @@ +use core::slice::from_raw_parts; + +use pinocchio::{ + account_info::AccountInfo, + instruction::{AccountMeta, Instruction, Signer}, + program::invoke_signed, + pubkey::Pubkey, + ProgramResult, +}; + +use crate::{write_bytes, UNINIT_BYTE}; + +#[repr(u8)] +#[derive(Clone, Copy)] +pub enum AuthorityType { + MintTokens = 0, + FreezeAccount = 1, + AccountOwner = 2, + CloseAccount = 3, +} + +/// Sets a new authority of a mint or account. +/// +/// ### Accounts: +/// 0. `[WRITE]` The mint or account to change the authority of. +/// 1. `[SIGNER]` The current authority of the mint or account. +pub struct SetAuthority<'a> { + /// Account (Mint or Token) + pub account: &'a AccountInfo, + + /// Authority of the Account. + pub authority: &'a AccountInfo, + + /// The type of authority to update. + pub authority_type: AuthorityType, + + /// The new authority + pub new_authority: Option<&'a Pubkey>, +} + +impl<'a> SetAuthority<'a> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + // account metadata + let account_metas: [AccountMeta; 2] = [ + AccountMeta::writable(self.account.key()), + AccountMeta::readonly_signer(self.authority.key()), + ]; + + // instruction data + // - [0]: instruction discriminator + // - [1]: authority_type + // - [2..35] new_authority + let mut instruction_data = [UNINIT_BYTE; 35]; + + // Set discriminator as u8 at offset [0] + write_bytes(&mut instruction_data, &[6]); + // Set authority_type as u8 at offset [1] + write_bytes(&mut instruction_data[1..2], &[self.authority_type as u8]); + // Set new_authority as [u8; 32] at offset [2..35] + if let Some(new_authority) = self.new_authority { + write_bytes(&mut instruction_data[2..3], &[1]); + write_bytes(&mut instruction_data[3..], new_authority); + } else { + write_bytes(&mut instruction_data[2..3], &[0]); + } + + let instruction = Instruction { + program_id: &crate::ID, + accounts: &account_metas, + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 35) }, + }; + + invoke_signed(&instruction, &[self.account, self.authority], signers) + } +} diff --git a/programs/token2022/src/instructions/sync_native.rs b/programs/token2022/src/instructions/sync_native.rs new file mode 100644 index 0000000..46a192e --- /dev/null +++ b/programs/token2022/src/instructions/sync_native.rs @@ -0,0 +1,37 @@ +use pinocchio::{ + account_info::AccountInfo, + instruction::{AccountMeta, Instruction, Signer}, + program::invoke_signed, + ProgramResult, +}; + +/// Given a native token account updates its amount field based +/// on the account's underlying `lamports`. +/// +/// ### Accounts: +/// 0. `[WRITE]` The native token account to sync with its underlying +/// lamports. +pub struct SyncNative<'a> { + /// Native Token Account + pub native_token: &'a AccountInfo, +} + +impl<'a> SyncNative<'a> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + // account metadata + let account_metas: [AccountMeta; 1] = [AccountMeta::writable(self.native_token.key())]; + + let instruction = Instruction { + program_id: &crate::ID, + accounts: &account_metas, + data: &[17], + }; + + invoke_signed(&instruction, &[self.native_token], signers) + } +} diff --git a/programs/token2022/src/instructions/thaw_account.rs b/programs/token2022/src/instructions/thaw_account.rs new file mode 100644 index 0000000..8353716 --- /dev/null +++ b/programs/token2022/src/instructions/thaw_account.rs @@ -0,0 +1,49 @@ +use pinocchio::{ + account_info::AccountInfo, + instruction::{AccountMeta, Instruction, Signer}, + program::invoke_signed, + ProgramResult, +}; + +/// Thaw a Frozen account using the Mint's freeze_authority +/// +/// ### Accounts: +/// 0. `[WRITE]` The account to thaw. +/// 1. `[]` The token mint. +/// 2. `[SIGNER]` The mint freeze authority. +pub struct ThawAccount<'a> { + /// Token Account to thaw. + pub token: &'a AccountInfo, + /// Mint Account. + pub mint: &'a AccountInfo, + /// Mint Freeze Authority Account + pub freeze_authority: &'a AccountInfo, +} + +impl<'a> ThawAccount<'a> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + // account metadata + let account_metas: [AccountMeta; 3] = [ + AccountMeta::writable(self.token.key()), + AccountMeta::readonly(self.mint.key()), + AccountMeta::readonly_signer(self.freeze_authority.key()), + ]; + + let instruction = Instruction { + program_id: &crate::ID, + accounts: &account_metas, + data: &[11], + }; + + invoke_signed( + &instruction, + &[self.token, self.mint, self.freeze_authority], + signers, + ) + } +} diff --git a/programs/token2022/src/instructions/transfer.rs b/programs/token2022/src/instructions/transfer.rs new file mode 100644 index 0000000..027b139 --- /dev/null +++ b/programs/token2022/src/instructions/transfer.rs @@ -0,0 +1,61 @@ +use core::slice::from_raw_parts; + +use pinocchio::{ + account_info::AccountInfo, + instruction::{AccountMeta, Instruction, Signer}, + program::invoke_signed, + ProgramResult, +}; + +use crate::{write_bytes, UNINIT_BYTE}; + +/// Transfer Tokens from one Token Account to another. +/// +/// ### Accounts: +/// 0. `[WRITE]` Sender account +/// 1. `[WRITE]` Recipient account +/// 2. `[SIGNER]` Authority account +pub struct Transfer<'a> { + /// Sender account. + pub from: &'a AccountInfo, + /// Recipient account. + pub to: &'a AccountInfo, + /// Authority account. + pub authority: &'a AccountInfo, + /// Amount of microtokens to transfer. + pub amount: u64, +} + +impl<'a> Transfer<'a> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + // account metadata + let account_metas: [AccountMeta; 3] = [ + AccountMeta::writable(self.from.key()), + AccountMeta::writable(self.to.key()), + AccountMeta::readonly_signer(self.authority.key()), + ]; + + // Instruction data layout: + // - [0]: instruction discriminator + // - [1..9]: amount + let mut instruction_data = [UNINIT_BYTE; 9]; + + // Set discriminator as u8 at offset [0] + write_bytes(&mut instruction_data, &[3]); + // Set amount as u64 at offset [1..9] + write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes()); + + let instruction = Instruction { + program_id: &crate::ID, + accounts: &account_metas, + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 9) }, + }; + + invoke_signed(&instruction, &[self.from, self.to, self.authority], signers) + } +} diff --git a/programs/token2022/src/instructions/transfer_checked.rs b/programs/token2022/src/instructions/transfer_checked.rs new file mode 100644 index 0000000..3ebe826 --- /dev/null +++ b/programs/token2022/src/instructions/transfer_checked.rs @@ -0,0 +1,70 @@ +use core::slice::from_raw_parts; + +use pinocchio::{ + account_info::AccountInfo, + instruction::{AccountMeta, Instruction, Signer}, + program::invoke_signed, + ProgramResult, +}; + +use crate::{write_bytes, UNINIT_BYTE}; + +/// Transfer Tokens from one Token Account to another. +/// +/// ### Accounts: +/// 0. `[WRITE]` The source account. +/// 1. `[]` The token mint. +/// 2. `[WRITE]` The destination account. +/// 3. `[SIGNER]` The source account's owner/delegate. +pub struct TransferChecked<'a> { + /// Sender account. + pub from: &'a AccountInfo, + /// Mint Account + pub mint: &'a AccountInfo, + /// Recipient account. + pub to: &'a AccountInfo, + /// Authority account. + pub authority: &'a AccountInfo, + /// Amount of microtokens to transfer. + pub amount: u64, + /// Decimal for the Token + pub decimals: u8, +} + +impl<'a> TransferChecked<'a> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + // account metadata + let account_metas: [AccountMeta; 4] = [ + AccountMeta::writable(self.from.key()), + AccountMeta::readonly(self.mint.key()), + AccountMeta::writable(self.to.key()), + AccountMeta::readonly_signer(self.authority.key()), + ]; + + // Instruction data layout: + // - [0]: instruction discriminator + // - [1..9]: amount + // - [9]: decimals + let mut instruction_data = [UNINIT_BYTE; 10]; + + // Set discriminator as u8 at offset [0] + write_bytes(&mut instruction_data, &[12]); + // Set amount as u64 at offset [1..9] + write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes()); + // Set decimals as u8 at offset [9] + write_bytes(&mut instruction_data[9..], &[self.decimals]); + + let instruction = Instruction { + program_id: &crate::ID, + accounts: &account_metas, + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 10) }, + }; + + invoke_signed(&instruction, &[self.from, self.to, self.authority], signers) + } +} diff --git a/programs/token2022/src/lib.rs b/programs/token2022/src/lib.rs new file mode 100644 index 0000000..70fc0c3 --- /dev/null +++ b/programs/token2022/src/lib.rs @@ -0,0 +1,23 @@ +#![no_std] + +pub mod extensions; +pub mod instructions; +pub mod state; + +pinocchio_pubkey::declare_id!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); + +use core::mem::MaybeUninit; + +const UNINIT_BYTE: MaybeUninit = MaybeUninit::::uninit(); + +pub unsafe fn from_bytes(data: &[u8]) -> T { + assert_eq!(data.len(), core::mem::size_of::()); + *(data.as_ptr() as *const T) +} + +#[inline(always)] +fn write_bytes(destination: &mut [MaybeUninit], source: &[u8]) { + for (d, s) in destination.iter_mut().zip(source.iter()) { + d.write(*s); + } +} diff --git a/programs/token2022/src/state/account_state.rs b/programs/token2022/src/state/account_state.rs new file mode 100644 index 0000000..5380c96 --- /dev/null +++ b/programs/token2022/src/state/account_state.rs @@ -0,0 +1,36 @@ +#[repr(u8)] +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum AccountState { + /// Account is not yet initialized + Uninitialized, + + /// Account is initialized; the account owner and/or delegate may perform + /// permitted operations on this account + Initialized, + + /// Account has been frozen by the mint freeze authority. Neither the + /// account owner nor the delegate are able to perform operations on + /// this account. + Frozen, +} + +impl From for AccountState { + fn from(value: u8) -> Self { + match value { + 0 => AccountState::Uninitialized, + 1 => AccountState::Initialized, + 2 => AccountState::Frozen, + _ => panic!("invalid account state value: {value}"), + } + } +} + +impl From for u8 { + fn from(value: AccountState) -> Self { + match value { + AccountState::Uninitialized => 0, + AccountState::Initialized => 1, + AccountState::Frozen => 2, + } + } +} diff --git a/programs/token2022/src/state/mint.rs b/programs/token2022/src/state/mint.rs new file mode 100644 index 0000000..25c1e6e --- /dev/null +++ b/programs/token2022/src/state/mint.rs @@ -0,0 +1,145 @@ +use pinocchio::{ + account_info::{AccountInfo, Ref}, + program_error::ProgramError, + pubkey::Pubkey, +}; + +use crate::ID; + +/// Mint data. +#[repr(C)] +pub struct Mint { + /// Indicates whether the mint authority is present or not. + mint_authority_flag: [u8; 4], + + /// Optional authority used to mint new tokens. The mint authority may only + /// be provided during mint creation. If no mint authority is present + /// then the mint has a fixed supply and no further tokens may be + /// minted. + mint_authority: Pubkey, + + /// Total supply of tokens. + supply: [u8; 8], + + /// Number of base 10 digits to the right of the decimal place. + decimals: u8, + + /// Is `true` if this structure has been initialized. + is_initialized: u8, + + /// Indicates whether the freeze authority is present or not. + freeze_authority_flag: [u8; 4], + + /// Optional authority to freeze token accounts. + freeze_authority: Pubkey, +} + +impl Mint { + /// The length of the `Mint` account data. + pub const LEN: usize = core::mem::size_of::(); + + /// Return a `Mint` from the given account info. + /// + /// This method performs owner and length validation on `AccountInfo`, safe borrowing + /// the account data. + #[inline] + pub fn from_account_info(account_info: &AccountInfo) -> Result, ProgramError> { + if account_info.data_len() != Self::LEN { + return Err(ProgramError::InvalidAccountData); + } + if account_info.owner() != &ID { + return Err(ProgramError::InvalidAccountOwner); + } + Ok(Ref::map(account_info.try_borrow_data()?, |data| unsafe { + Self::from_bytes(data) + })) + } + + /// Return a `Mint` from the given account info. + /// + /// This method performs owner and length validation on `AccountInfo`, but does not + /// perform the borrow check. + /// + /// # Safety + /// + /// The caller must ensure that it is safe to borrow the account data – e.g., there are + /// no mutable borrows of the account data. + #[inline] + pub unsafe fn from_account_info_unchecked( + account_info: &AccountInfo, + ) -> Result<&Self, ProgramError> { + if account_info.data_len() != Self::LEN { + return Err(ProgramError::InvalidAccountData); + } + if account_info.owner() != &ID { + return Err(ProgramError::InvalidAccountOwner); + } + Ok(Self::from_bytes(account_info.borrow_data_unchecked())) + } + + /// Return a `Mint` from the given bytes. + /// + /// # Safety + /// + /// The caller must ensure that `bytes` contains a valid representation of `Mint`. + #[inline(always)] + pub unsafe fn from_bytes(bytes: &[u8]) -> &Self { + &*(bytes.as_ptr() as *const Mint) + } + + #[inline(always)] + pub fn has_mint_authority(&self) -> bool { + self.mint_authority_flag[0] == 1 + } + + pub fn mint_authority(&self) -> Option<&Pubkey> { + if self.has_mint_authority() { + Some(self.mint_authority_unchecked()) + } else { + None + } + } + + /// Return the mint authority. + /// + /// This method should be used when the caller knows that the mint will have a mint + /// authority set since it skips the `Option` check. + #[inline(always)] + pub fn mint_authority_unchecked(&self) -> &Pubkey { + &self.mint_authority + } + + pub fn supply(&self) -> u64 { + unsafe { core::ptr::read_unaligned(self.supply.as_ptr() as *const u64) } + } + + pub fn decimals(&self) -> u8 { + self.decimals + } + + pub fn is_initialized(&self) -> bool { + self.is_initialized == 1 + } + + #[inline(always)] + pub fn has_freeze_authority(&self) -> bool { + self.freeze_authority_flag[0] == 1 + } + + pub fn freeze_authority(&self) -> Option<&Pubkey> { + if self.has_freeze_authority() { + Some(self.freeze_authority_unchecked()) + } else { + None + } + } + + /// Return the freeze authority. + /// + /// This method should be used when the caller knows that the mint will have a freeze + /// authority set since it skips the `Option` check. + #[inline(always)] + pub fn freeze_authority_unchecked(&self) -> &Pubkey { + &self.freeze_authority + } +} diff --git a/programs/token2022/src/state/mod.rs b/programs/token2022/src/state/mod.rs new file mode 100644 index 0000000..1cbe1a1 --- /dev/null +++ b/programs/token2022/src/state/mod.rs @@ -0,0 +1,8 @@ +pub mod token; +pub use token::*; + +pub mod mint; +pub use mint::*; + +pub mod account_state; +pub use account_state::*; diff --git a/programs/token2022/src/state/token.rs b/programs/token2022/src/state/token.rs new file mode 100644 index 0000000..2250a21 --- /dev/null +++ b/programs/token2022/src/state/token.rs @@ -0,0 +1,198 @@ +use super::AccountState; +use pinocchio::{ + account_info::{AccountInfo, Ref}, + program_error::ProgramError, + pubkey::Pubkey, +}; + +use crate::ID; + +/// Token account data. +#[repr(C)] +pub struct TokenAccount { + /// The mint associated with this account + mint: Pubkey, + + /// The owner of this account. + owner: Pubkey, + + /// The amount of tokens this account holds. + amount: [u8; 8], + + /// Indicates whether the delegate is present or not. + delegate_flag: [u8; 4], + + /// If `delegate` is `Some` then `delegated_amount` represents + /// the amount authorized by the delegate. + delegate: Pubkey, + + /// The account's state. + state: u8, + + /// Indicates whether this account represents a native token or not. + is_native: [u8; 4], + + /// If is_native.is_some, this is a native token, and the value logs the + /// rent-exempt reserve. An Account is required to be rent-exempt, so + /// the value is used by the Processor to ensure that wrapped SOL + /// accounts do not drop below this threshold. + native_amount: [u8; 8], + + /// The amount delegated. + delegated_amount: [u8; 8], + + /// Indicates whether the close authority is present or not. + close_authority_flag: [u8; 4], + + /// Optional authority to close the account. + close_authority: Pubkey, +} + +impl TokenAccount { + pub const LEN: usize = core::mem::size_of::(); + + /// Return a `TokenAccount` from the given account info. + /// + /// This method performs owner and length validation on `AccountInfo`, safe borrowing + /// the account data. + #[inline] + pub fn from_account_info( + account_info: &AccountInfo, + ) -> Result, ProgramError> { + if account_info.data_len() != Self::LEN { + return Err(ProgramError::InvalidAccountData); + } + if account_info.owner() != &ID { + return Err(ProgramError::InvalidAccountData); + } + Ok(Ref::map(account_info.try_borrow_data()?, |data| unsafe { + Self::from_bytes(data) + })) + } + + /// Return a `TokenAccount` from the given account info. + /// + /// This method performs owner and length validation on `AccountInfo`, but does not + /// perform the borrow check. + /// + /// # Safety + /// + /// The caller must ensure that it is safe to borrow the account data – e.g., there are + /// no mutable borrows of the account data. + #[inline] + pub unsafe fn from_account_info_unchecked( + account_info: &AccountInfo, + ) -> Result<&TokenAccount, ProgramError> { + if account_info.data_len() != Self::LEN { + return Err(ProgramError::InvalidAccountData); + } + if account_info.owner() != &ID { + return Err(ProgramError::InvalidAccountData); + } + Ok(Self::from_bytes(account_info.borrow_data_unchecked())) + } + + /// Return a `TokenAccount` from the given bytes. + /// + /// # Safety + /// + /// The caller must ensure that `bytes` contains a valid representation of `TokenAccount`. + #[inline(always)] + pub unsafe fn from_bytes(bytes: &[u8]) -> &Self { + &*(bytes.as_ptr() as *const TokenAccount) + } + + pub fn mint(&self) -> &Pubkey { + &self.mint + } + + pub fn owner(&self) -> &Pubkey { + &self.owner + } + + pub fn amount(&self) -> u64 { + unsafe { core::ptr::read_unaligned(self.amount.as_ptr() as *const u64) } + } + + #[inline(always)] + pub fn has_delegate(&self) -> bool { + self.delegate_flag[0] == 1 + } + + pub fn delegate(&self) -> Option<&Pubkey> { + if self.has_delegate() { + Some(self.delegate_unchecked()) + } else { + None + } + } + + /// Use this when you know the account will have a delegate and want to skip the `Option` check. + #[inline(always)] + pub fn delegate_unchecked(&self) -> &Pubkey { + &self.delegate + } + + #[inline(always)] + pub fn state(&self) -> AccountState { + self.state.into() + } + + #[inline(always)] + pub fn is_native(&self) -> bool { + self.is_native[0] == 1 + } + + pub fn native_amount(&self) -> Option { + if self.is_native() { + Some(self.native_amount_unchecked()) + } else { + None + } + } + + /// Return the native amount. + /// + /// This method should be used when the caller knows that the token is native since it + /// skips the `Option` check. + #[inline(always)] + pub fn native_amount_unchecked(&self) -> u64 { + unsafe { core::ptr::read_unaligned(self.native_amount.as_ptr() as *const u64) } + } + + pub fn delegated_amount(&self) -> u64 { + unsafe { core::ptr::read_unaligned(self.delegated_amount.as_ptr() as *const u64) } + } + + #[inline(always)] + pub fn has_close_authority(&self) -> bool { + self.close_authority_flag[0] == 1 + } + + pub fn close_authority(&self) -> Option<&Pubkey> { + if self.has_close_authority() { + Some(self.close_authority_unchecked()) + } else { + None + } + } + + /// Return the close authority. + /// + /// This method should be used when the caller knows that the token will have a close + /// authority set since it skips the `Option` check. + #[inline(always)] + pub fn close_authority_unchecked(&self) -> &Pubkey { + &self.close_authority + } + + #[inline(always)] + pub fn is_initialized(&self) -> bool { + self.state != AccountState::Uninitialized as u8 + } + + #[inline(always)] + pub fn is_frozen(&self) -> bool { + self.state == AccountState::Frozen as u8 + } +} From 2e9da414d58702a81641317555ed605a29d71300 Mon Sep 17 00:00:00 2001 From: NagaprasadVr Date: Thu, 14 Nov 2024 08:31:56 +0530 Subject: [PATCH 05/13] rebase --- programs/token/src/instructions/approve.rs | 13 +-- .../token/src/instructions/approve_checked.rs | 15 +-- programs/token/src/instructions/burn.rs | 13 +-- .../token/src/instructions/burn_checked.rs | 15 +-- .../src/instructions/initialize_account_2.rs | 10 +- .../src/instructions/initialize_account_3.rs | 10 +- .../token/src/instructions/initialize_mint.rs | 19 ++-- .../src/instructions/initialize_mint_2.rs | 19 ++-- programs/token/src/instructions/mint_to.rs | 11 +- .../token/src/instructions/mint_to_checked.rs | 12 ++- .../token/src/instructions/set_authority.rs | 17 ++- programs/token/src/instructions/transfer.rs | 10 +- .../src/instructions/transfer_checked.rs | 12 ++- programs/token/src/lib.rs | 100 ++---------------- .../token2022/src/extensions/transfer_fee.rs | 86 +++++++++++++-- programs/token2022/src/lib.rs | 2 +- 16 files changed, 192 insertions(+), 172 deletions(-) diff --git a/programs/token/src/instructions/approve.rs b/programs/token/src/instructions/approve.rs index 96278f5..d9596f7 100644 --- a/programs/token/src/instructions/approve.rs +++ b/programs/token/src/instructions/approve.rs @@ -1,3 +1,5 @@ +use core::slice::from_raw_parts; + use pinocchio::{ account_info::AccountInfo, instruction::{AccountMeta, Instruction, Signer}, @@ -5,7 +7,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{IxData, UNINIT_BYTE}; +use crate::{write_bytes, UNINIT_BYTE}; /// Approves a delegate. /// @@ -41,18 +43,17 @@ impl<'a> Approve<'a> { // Instruction data // - [0]: instruction discriminator (1 byte, u8) // - [1..9]: amount (8 bytes, u64) - let mut ix_buffer = [UNINIT_BYTE; 9]; - let mut ix_data = IxData::new(&mut ix_buffer); + let mut instruction_data = [UNINIT_BYTE; 9]; // Set discriminator as u8 at offset [0] - ix_data.write_bytes(&[4]); + write_bytes(&mut instruction_data, &[4]); // Set amount as u64 at offset [1..9] - ix_data.write_bytes(&self.amount.to_le_bytes()); + write_bytes(&mut instruction_data[1..], &self.amount.to_le_bytes()); let instruction = Instruction { program_id: &crate::ID, accounts: &account_metas, - data: ix_data.read_bytes(), + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 9) }, }; invoke_signed( diff --git a/programs/token/src/instructions/approve_checked.rs b/programs/token/src/instructions/approve_checked.rs index 50da319..6195122 100644 --- a/programs/token/src/instructions/approve_checked.rs +++ b/programs/token/src/instructions/approve_checked.rs @@ -1,3 +1,5 @@ +use core::slice::from_raw_parts; + use pinocchio::{ account_info::AccountInfo, instruction::{AccountMeta, Instruction, Signer}, @@ -5,7 +7,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{IxData, UNINIT_BYTE}; +use crate::{write_bytes, UNINIT_BYTE}; /// Approves a delegate. /// @@ -48,20 +50,19 @@ impl<'a> ApproveChecked<'a> { // - [0] : instruction discriminator (1 byte, u8) // - [1..9]: amount (8 bytes, u64) // - [9] : decimals (1 byte, u8) - let mut ix_buffer = [UNINIT_BYTE; 10]; - let mut ix_data = IxData::new(&mut ix_buffer); + let mut instruction_data = [UNINIT_BYTE; 10]; // Set discriminator as u8 at offset [0] - ix_data.write_bytes(&[13]); + write_bytes(&mut instruction_data, &[13]); // Set amount as u64 at offset [1..9] - ix_data.write_bytes(&self.amount.to_le_bytes()); + write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes()); // Set decimals as u8 at offset [9] - ix_data.write_bytes(&[self.decimals]); + write_bytes(&mut instruction_data[9..], &[self.decimals]); let instruction = Instruction { program_id: &crate::ID, accounts: &account_metas, - data: ix_data.read_bytes(), + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 10) }, }; invoke_signed( diff --git a/programs/token/src/instructions/burn.rs b/programs/token/src/instructions/burn.rs index 290d6ba..4a8e71a 100644 --- a/programs/token/src/instructions/burn.rs +++ b/programs/token/src/instructions/burn.rs @@ -1,3 +1,5 @@ +use core::slice::from_raw_parts; + use pinocchio::{ account_info::AccountInfo, instruction::{AccountMeta, Instruction, Signer}, @@ -5,7 +7,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{IxData, UNINIT_BYTE}; +use crate::{write_bytes, UNINIT_BYTE}; /// Burns tokens by removing them from an account. /// @@ -41,18 +43,17 @@ impl<'a> Burn<'a> { // Instruction data // - [0]: instruction discriminator (1 byte, u8) // - [1..9]: amount (8 bytes, u64) - let mut ix_buffer = [UNINIT_BYTE; 9]; - let mut ix_data = IxData::new(&mut ix_buffer); + let mut instruction_data = [UNINIT_BYTE; 9]; // Set discriminator as u8 at offset [0] - ix_data.write_bytes(&[8]); + write_bytes(&mut instruction_data, &[8]); // Set amount as u64 at offset [1..9] - ix_data.write_bytes(&self.amount.to_le_bytes()); + write_bytes(&mut instruction_data[1..], &self.amount.to_le_bytes()); let instruction = Instruction { program_id: &crate::ID, accounts: &account_metas, - data: ix_data.read_bytes(), + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 9) }, }; invoke_signed( diff --git a/programs/token/src/instructions/burn_checked.rs b/programs/token/src/instructions/burn_checked.rs index fa820bd..789a320 100644 --- a/programs/token/src/instructions/burn_checked.rs +++ b/programs/token/src/instructions/burn_checked.rs @@ -1,4 +1,6 @@ -use crate::{IxData, UNINIT_BYTE}; +use core::slice::from_raw_parts; + +use crate::{write_bytes, UNINIT_BYTE}; use pinocchio::{ account_info::AccountInfo, instruction::{AccountMeta, Instruction, Signer}, @@ -43,20 +45,19 @@ impl<'a> BurnChecked<'a> { // - [0]: instruction discriminator (1 byte, u8) // - [1..9]: amount (8 bytes, u64) // - [9]: decimals (1 byte, u8) - let mut ix_buffer = [UNINIT_BYTE; 10]; - let mut ix_data = IxData::new(&mut ix_buffer); + let mut instruction_data = [UNINIT_BYTE; 10]; // Set discriminator as u8 at offset [0] - ix_data.write_bytes(&[15]); + write_bytes(&mut instruction_data, &[15]); // Set amount as u64 at offset [1..9] - ix_data.write_bytes(&self.amount.to_le_bytes()); + write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes()); // Set decimals as u8 at offset [9] - ix_data.write_bytes(&[self.decimals]); + write_bytes(&mut instruction_data[9..], &[self.decimals]); let instruction = Instruction { program_id: &crate::ID, accounts: &account_metas, - data: ix_data.read_bytes(), + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 10) }, }; invoke_signed( diff --git a/programs/token/src/instructions/initialize_account_2.rs b/programs/token/src/instructions/initialize_account_2.rs index 37374ad..9ca72c7 100644 --- a/programs/token/src/instructions/initialize_account_2.rs +++ b/programs/token/src/instructions/initialize_account_2.rs @@ -1,3 +1,5 @@ +use core::slice::from_raw_parts; + use pinocchio::{ account_info::AccountInfo, instruction::{AccountMeta, Instruction, Signer}, @@ -6,7 +8,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{IxData, UNINIT_BYTE}; +use crate::{write_bytes, UNINIT_BYTE}; /// Initialize a new Token Account. /// @@ -45,14 +47,14 @@ impl<'a> InitilizeAccount2<'a> { let mut instruction_data = [UNINIT_BYTE; 33]; // Set discriminator as u8 at offset [0] - ix_data.write_bytes(&[16]); + write_bytes(&mut instruction_data, &[16]); // Set owner as [u8; 32] at offset [1..33] - ix_data.write_bytes(self.owner.as_ref()); + write_bytes(&mut instruction_data[1..], self.owner); let instruction = Instruction { program_id: &crate::ID, accounts: &account_metas, - data: ix_data.read_bytes(), + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 33) }, }; invoke_signed( diff --git a/programs/token/src/instructions/initialize_account_3.rs b/programs/token/src/instructions/initialize_account_3.rs index 4fac6d6..f3dc433 100644 --- a/programs/token/src/instructions/initialize_account_3.rs +++ b/programs/token/src/instructions/initialize_account_3.rs @@ -1,3 +1,5 @@ +use core::slice::from_raw_parts; + use pinocchio::{ account_info::AccountInfo, instruction::{AccountMeta, Instruction, Signer}, @@ -6,7 +8,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{IxData, UNINIT_BYTE}; +use crate::{write_bytes, UNINIT_BYTE}; /// Initialize a new Token Account. /// @@ -41,14 +43,14 @@ impl<'a> InitilizeAccount3<'a> { let mut instruction_data = [UNINIT_BYTE; 33]; // Set discriminator as u8 at offset [0] - ix_data.write_bytes(&[18]); + write_bytes(&mut instruction_data, &[18]); // Set owner as [u8; 32] at offset [1..33] - ix_data.write_bytes(self.owner.as_ref()); + write_bytes(&mut instruction_data[1..], self.owner); let instruction = Instruction { program_id: &crate::ID, accounts: &account_metas, - data: ix_data.read_bytes(), + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 33) }, }; invoke_signed(&instruction, &[self.token, self.mint], signers) diff --git a/programs/token/src/instructions/initialize_mint.rs b/programs/token/src/instructions/initialize_mint.rs index da140a5..3cf1fa3 100644 --- a/programs/token/src/instructions/initialize_mint.rs +++ b/programs/token/src/instructions/initialize_mint.rs @@ -1,3 +1,5 @@ +use core::slice::from_raw_parts; + use pinocchio::{ account_info::AccountInfo, instruction::{AccountMeta, Instruction, Signer}, @@ -6,7 +8,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{FromOptPubkeyToOptBytes, IxData, UNINIT_BYTE}; +use crate::{write_bytes, UNINIT_BYTE}; /// Initialize a new mint. /// @@ -48,18 +50,23 @@ impl<'a> InitilizeMint<'a> { let mut instruction_data = [UNINIT_BYTE; 67]; // Set discriminator as u8 at offset [0] - ix_data.write_bytes(&[0]); + write_bytes(&mut instruction_data, &[0]); // Set decimals as u8 at offset [1] - ix_data.write_bytes(&[self.decimals]); + write_bytes(&mut instruction_data[1..2], &[self.decimals]); // Set mint_authority as Pubkey at offset [2..34] - ix_data.write_bytes(self.mint_authority.as_ref()); + write_bytes(&mut instruction_data[2..34], self.mint_authority); // Set COption & freeze_authority at offset [34..67] - ix_data.write_optional_bytes(self.freeze_authority.to_opt_slice()); + if let Some(freeze_auth) = self.freeze_authority { + write_bytes(&mut instruction_data[34..35], &[1]); + write_bytes(&mut instruction_data[35..], freeze_auth); + } else { + write_bytes(&mut instruction_data[34..35], &[0]); + } let instruction = Instruction { program_id: &crate::ID, accounts: &account_metas, - data: ix_data.read_bytes(), + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 67) }, }; invoke_signed(&instruction, &[self.mint, self.rent_sysvar], signers) diff --git a/programs/token/src/instructions/initialize_mint_2.rs b/programs/token/src/instructions/initialize_mint_2.rs index d3bd898..51954be 100644 --- a/programs/token/src/instructions/initialize_mint_2.rs +++ b/programs/token/src/instructions/initialize_mint_2.rs @@ -1,3 +1,5 @@ +use core::slice::from_raw_parts; + use pinocchio::{ account_info::AccountInfo, instruction::{AccountMeta, Instruction, Signer}, @@ -6,7 +8,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{FromOptPubkeyToOptBytes, IxData, UNINIT_BYTE}; +use crate::{write_bytes, UNINIT_BYTE}; /// Initialize a new mint. /// @@ -42,18 +44,23 @@ impl<'a> InitilizeMint2<'a> { let mut instruction_data = [UNINIT_BYTE; 67]; // Set discriminator as u8 at offset [0] - ix_data.write_bytes(&[20]); + write_bytes(&mut instruction_data, &[20]); // Set decimals as u8 at offset [1] - ix_data.write_bytes(&[self.decimals]); + write_bytes(&mut instruction_data[1..2], &[self.decimals]); // Set mint_authority as Pubkey at offset [2..34] - ix_data.write_bytes(self.mint_authority.as_ref()); + write_bytes(&mut instruction_data[2..34], self.mint_authority); // Set COption & freeze_authority at offset [34..67] - ix_data.write_optional_bytes(self.freeze_authority.to_opt_slice()); + if let Some(freeze_auth) = self.freeze_authority { + write_bytes(&mut instruction_data[34..35], &[1]); + write_bytes(&mut instruction_data[35..], freeze_auth); + } else { + write_bytes(&mut instruction_data[34..35], &[0]); + } let instruction = Instruction { program_id: &crate::ID, accounts: &account_metas, - data: ix_data.read_bytes(), + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 67) }, }; invoke_signed(&instruction, &[self.mint], signers) diff --git a/programs/token/src/instructions/mint_to.rs b/programs/token/src/instructions/mint_to.rs index 5325b56..e0036db 100644 --- a/programs/token/src/instructions/mint_to.rs +++ b/programs/token/src/instructions/mint_to.rs @@ -1,3 +1,5 @@ +use core::slice::from_raw_parts; + use pinocchio::{ account_info::AccountInfo, instruction::{AccountMeta, Instruction, Signer}, @@ -5,7 +7,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{IxData, UNINIT_BYTE}; +use crate::{write_bytes, UNINIT_BYTE}; /// Mints new tokens to an account. /// @@ -45,15 +47,14 @@ impl<'a> MintTo<'a> { let mut instruction_data = [UNINIT_BYTE; 9]; // Set discriminator as u8 at offset [0] - ix_data.write_bytes(&[7]); - + write_bytes(&mut instruction_data, &[7]); // Set amount as u64 at offset [1..9] - ix_data.write_bytes(&self.amount.to_le_bytes()); + write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes()); let instruction = Instruction { program_id: &crate::ID, accounts: &account_metas, - data: ix_data.read_bytes(), + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 9) }, }; invoke_signed( diff --git a/programs/token/src/instructions/mint_to_checked.rs b/programs/token/src/instructions/mint_to_checked.rs index 031933a..32e30b6 100644 --- a/programs/token/src/instructions/mint_to_checked.rs +++ b/programs/token/src/instructions/mint_to_checked.rs @@ -1,3 +1,5 @@ +use core::slice::from_raw_parts; + use pinocchio::{ account_info::AccountInfo, instruction::{AccountMeta, Instruction, Signer}, @@ -5,7 +7,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{IxData, UNINIT_BYTE}; +use crate::{write_bytes, UNINIT_BYTE}; /// Mints new tokens to an account. /// @@ -48,16 +50,16 @@ impl<'a> MintToChecked<'a> { let mut instruction_data = [UNINIT_BYTE; 10]; // Set discriminator as u8 at offset [0] - ix_data.write_bytes(&[14]); + write_bytes(&mut instruction_data, &[14]); // Set amount as u64 at offset [1..9] - ix_data.write_bytes(&self.amount.to_le_bytes()); + write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes()); // Set decimals as u8 at offset [9] - ix_data.write_bytes(&[self.decimals]); + write_bytes(&mut instruction_data[9..], &[self.decimals]); let instruction = Instruction { program_id: &crate::ID, accounts: &account_metas, - data: ix_data.read_bytes(), + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 10) }, }; invoke_signed( diff --git a/programs/token/src/instructions/set_authority.rs b/programs/token/src/instructions/set_authority.rs index 21ba214..2ded29b 100644 --- a/programs/token/src/instructions/set_authority.rs +++ b/programs/token/src/instructions/set_authority.rs @@ -1,3 +1,5 @@ +use core::slice::from_raw_parts; + use pinocchio::{ account_info::AccountInfo, instruction::{AccountMeta, Instruction, Signer}, @@ -6,7 +8,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{FromOptPubkeyToOptBytes, IxData, UNINIT_BYTE}; +use crate::{write_bytes, UNINIT_BYTE}; #[repr(u8)] #[derive(Clone, Copy)] @@ -57,16 +59,21 @@ impl<'a> SetAuthority<'a> { let mut instruction_data = [UNINIT_BYTE; 35]; // Set discriminator as u8 at offset [0] - ix_data.write_bytes(&[6]); + write_bytes(&mut instruction_data, &[6]); // Set authority_type as u8 at offset [1] - ix_data.write_bytes(&[self.authority_type as u8]); + write_bytes(&mut instruction_data[1..2], &[self.authority_type as u8]); // Set new_authority as [u8; 32] at offset [2..35] - ix_data.write_optional_bytes(self.new_authority.to_opt_slice()); + if let Some(new_authority) = self.new_authority { + write_bytes(&mut instruction_data[2..3], &[1]); + write_bytes(&mut instruction_data[3..], new_authority); + } else { + write_bytes(&mut instruction_data[2..3], &[0]); + } let instruction = Instruction { program_id: &crate::ID, accounts: &account_metas, - data: ix_data.read_bytes(), + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 35) }, }; invoke_signed(&instruction, &[self.account, self.authority], signers) diff --git a/programs/token/src/instructions/transfer.rs b/programs/token/src/instructions/transfer.rs index 17267e4..ad58ce1 100644 --- a/programs/token/src/instructions/transfer.rs +++ b/programs/token/src/instructions/transfer.rs @@ -1,3 +1,5 @@ +use core::slice::from_raw_parts; + use pinocchio::{ account_info::AccountInfo, instruction::{AccountMeta, Instruction, Signer}, @@ -5,7 +7,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{IxData, UNINIT_BYTE}; +use crate::{write_bytes, UNINIT_BYTE}; /// Transfer Tokens from one Token Account to another. /// @@ -44,14 +46,14 @@ impl<'a> Transfer<'a> { let mut instruction_data = [UNINIT_BYTE; 9]; // Set discriminator as u8 at offset [0] - ix_data.write_bytes(&[3]); + write_bytes(&mut instruction_data, &[3]); // Set amount as u64 at offset [1..9] - ix_data.write_bytes(&self.amount.to_le_bytes()); + write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes()); let instruction = Instruction { program_id: &crate::ID, accounts: &account_metas, - data: ix_data.read_bytes(), + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 9) }, }; invoke_signed(&instruction, &[self.from, self.to, self.authority], signers) diff --git a/programs/token/src/instructions/transfer_checked.rs b/programs/token/src/instructions/transfer_checked.rs index 3f7f351..c4ded10 100644 --- a/programs/token/src/instructions/transfer_checked.rs +++ b/programs/token/src/instructions/transfer_checked.rs @@ -1,3 +1,5 @@ +use core::slice::from_raw_parts; + use pinocchio::{ account_info::AccountInfo, instruction::{AccountMeta, Instruction, Signer}, @@ -5,7 +7,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{IxData, UNINIT_BYTE}; +use crate::{write_bytes, UNINIT_BYTE}; /// Transfer Tokens from one Token Account to another. /// @@ -51,16 +53,16 @@ impl<'a> TransferChecked<'a> { let mut instruction_data = [UNINIT_BYTE; 10]; // Set discriminator as u8 at offset [0] - ix_data.write_bytes(&[12]); + write_bytes(&mut instruction_data, &[12]); // Set amount as u64 at offset [1..9] - ix_data.write_bytes(&self.amount.to_le_bytes()); + write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes()); // Set decimals as u8 at offset [9] - ix_data.write_bytes(&[self.decimals]); + write_bytes(&mut instruction_data[9..], &[self.decimals]); let instruction = Instruction { program_id: &crate::ID, accounts: &account_metas, - data: ix_data.read_bytes(), + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 10) }, }; invoke_signed(&instruction, &[self.from, self.to, self.authority], signers) diff --git a/programs/token/src/lib.rs b/programs/token/src/lib.rs index 534b58f..6ad2eb9 100644 --- a/programs/token/src/lib.rs +++ b/programs/token/src/lib.rs @@ -7,102 +7,16 @@ pinocchio_pubkey::declare_id!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); use core::mem::MaybeUninit; -use pinocchio::pubkey::PUBKEY_BYTES; - const UNINIT_BYTE: MaybeUninit = MaybeUninit::::uninit(); -pub trait FromOptPubkeyToOptBytes { - fn to_opt_slice(&self) -> Option<&[u8]>; -} - -impl FromOptPubkeyToOptBytes for Option<&[u8; PUBKEY_BYTES]> { - #[inline(always)] - fn to_opt_slice(&self) -> Option<&[u8]> { - self.map(|pubkey| pubkey.as_ref()) - } -} - -pub struct IxData<'i> { - pub bytes: &'i mut [MaybeUninit], - pub current_size: usize, - pub capacity: usize, +pub unsafe fn from_bytes(data: &[u8]) -> T { + assert_eq!(data.len(), core::mem::size_of::()); + *(data.as_ptr() as *const T) } -impl<'i> IxData<'i> { - #[inline(always)] - pub fn new(data: &'i mut [MaybeUninit]) -> Self { - let capacity = data.len(); - Self { - bytes: data, - current_size: 0, - capacity, - } - } - - #[inline(always)] - pub fn write_bytes(&mut self, source: &[u8]) { - if self.current_size + source.len() > self.capacity { - return; - } - - let start = self.current_size; - let end = start + source.len(); - - for (d, s) in &mut self.bytes[start..end].iter_mut().zip(source.iter()) { - d.write(*s); - } - - self.current_size += source.len(); - } - - #[inline(always)] - pub fn write_optional_bytes(&mut self, source: Option<&[u8]>) { - if let Some(source) = source { - self.write_bytes(&[1]); - self.write_bytes(source); - } else { - self.write_bytes(&[0]); - } - } - - #[inline(always)] - pub fn read_bytes(&self) -> &[u8] { - unsafe { core::slice::from_raw_parts(self.bytes.as_ptr() as *const u8, self.current_size) } - } -} - -#[cfg(test)] -mod tests { - use crate::FromOptPubkeyToOptBytes; - - #[test] - fn test_write_bytes() { - let mut data = [crate::UNINIT_BYTE; 50]; - - let mut ix_data = crate::IxData::new(&mut data); - - assert_eq!(ix_data.current_size, 0); - - assert_eq!(ix_data.capacity, 50); - - ix_data.write_bytes(&[1, 2, 3, 4]); - - let optional_pubkey: Option<&[u8; 32]> = Some(&[2; 32]); - - ix_data.write_optional_bytes(optional_pubkey.to_opt_slice()); - - assert!(ix_data.current_size == 37); - - ix_data.write_bytes(&[5, 6, 7, 8, 9, 10]); - - assert!(ix_data.current_size == 43); - - assert_eq!( - ix_data.read_bytes(), - &[ - 1, 2, 3, 4, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 6, 7, 8, 9, 10 - ] - ); +#[inline(always)] +fn write_bytes(destination: &mut [MaybeUninit], source: &[u8]) { + for (d, s) in destination.iter_mut().zip(source.iter()) { + d.write(*s); } } diff --git a/programs/token2022/src/extensions/transfer_fee.rs b/programs/token2022/src/extensions/transfer_fee.rs index ec4a6e7..e6bbfb8 100644 --- a/programs/token2022/src/extensions/transfer_fee.rs +++ b/programs/token2022/src/extensions/transfer_fee.rs @@ -1,14 +1,15 @@ use core::slice::from_raw_parts; use pinocchio::{ - account_info::AccountInfo, + account_info::{AccountInfo, Ref}, instruction::{AccountMeta, Instruction, Signer}, program::invoke_signed, + program_error::ProgramError, pubkey::Pubkey, ProgramResult, }; -use crate::{write_bytes, UNINIT_BYTE}; +use crate::{write_bytes, ID, UNINIT_BYTE}; /// State @@ -42,6 +43,62 @@ pub struct TransferFeeConfig { pub newer_transfer_fee: TransferFee, } +impl TransferFeeConfig { + /// The length of the `TransferFeeConfig` account data. + pub const LEN: usize = core::mem::size_of::(); + + /// Return a `TransferFeeConfig` from the given account info. + /// + /// This method performs owner and length validation on `AccountInfo`, safe borrowing + /// the account data. + #[inline(always)] + pub fn from_account_info( + account_info: &AccountInfo, + ) -> Result, ProgramError> { + if account_info.data_len() != Self::LEN { + return Err(ProgramError::InvalidAccountData); + } + if account_info.owner() != &ID { + return Err(ProgramError::InvalidAccountOwner); + } + Ok(Ref::map(account_info.try_borrow_data()?, |data| unsafe { + Self::from_bytes(data) + })) + } + + /// Return a `TransferFeeConfig` from the given account info. + /// + /// This method performs owner and length validation on `AccountInfo`, but does not + /// perform the borrow check. + /// + /// # Safety + /// + /// The caller must ensure that it is safe to borrow the account data – e.g., there are + /// no mutable borrows of the account data. + #[inline] + pub unsafe fn from_account_info_unchecked( + account_info: &AccountInfo, + ) -> Result<&Self, ProgramError> { + if account_info.data_len() != Self::LEN { + return Err(ProgramError::InvalidAccountData); + } + if account_info.owner() != &ID { + return Err(ProgramError::InvalidAccountOwner); + } + Ok(Self::from_bytes(account_info.borrow_data_unchecked())) + } + + /// Return a `TransferFeeConfig` from the given bytes. + /// + /// # Safety + /// + /// The caller must ensure that `bytes` contains a valid representation of `TransferFeeConfig`. + #[inline(always)] + pub unsafe fn from_bytes(bytes: &[u8]) -> &Self { + &*(bytes.as_ptr() as *const TransferFeeConfig) + } +} + /// Instruction pub struct InitializeTransferFeeConfig<'a> { @@ -75,27 +132,40 @@ impl<'a> InitializeTransferFeeConfig<'a> { // - [99..101]: transfer_fee_basis_points // - [101..109]: maximum_fee - let mut instruction_data = [UNINIT_BYTE; 83]; + let mut instruction_data = [UNINIT_BYTE; 109]; // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[todo!()]); + write_bytes(&mut instruction_data, &[27]); // Set mint as Pubkey at offset [1..33] write_bytes(&mut instruction_data[1..33], self.mint.key().as_ref()); // Set transfer_fee_config_authority COption at offset [33..37] + let mut offset = 33; if let Some(transfer_fee_config_authority) = self.transfer_fee_config_authority { - write_bytes(&mut instruction_data[33..37], &[1]); + write_bytes(&mut instruction_data[33..34], &[1]); write_bytes( - &mut instruction_data[37..69], + &mut instruction_data[34..66], transfer_fee_config_authority.as_ref(), ); + offset += 33; + } else { + write_bytes(&mut instruction_data[33..34], &[0]); + offset += 1; + } + + if let Some(withdraw_withheld_authority) = self.withdraw_withheld_authority { + write_bytes(&mut instruction_data[offset..offset + 1], &[1]); + write_bytes( + &mut instruction_data[(offset + 1)..(offset + 1 + 32)], + withdraw_withheld_authority.as_ref(), + ); } else { - write_bytes(&mut instruction_data[33..37], &[0]); + write_bytes(&mut instruction_data[offset..offset + 33], &[0]); } let instruction = Instruction { program_id: &crate::ID, accounts: &[AccountMeta::writable(self.mint.key())], - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 9) }, + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 109) }, }; invoke_signed(&instruction, &[self.mint], signers) diff --git a/programs/token2022/src/lib.rs b/programs/token2022/src/lib.rs index 70fc0c3..a2d04ef 100644 --- a/programs/token2022/src/lib.rs +++ b/programs/token2022/src/lib.rs @@ -4,7 +4,7 @@ pub mod extensions; pub mod instructions; pub mod state; -pinocchio_pubkey::declare_id!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); +pinocchio_pubkey::declare_id!("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"); use core::mem::MaybeUninit; From 957479a8fc0328ee419ecbb2fde9d879513117fe Mon Sep 17 00:00:00 2001 From: NagaprasadVr Date: Thu, 14 Nov 2024 11:21:18 +0530 Subject: [PATCH 06/13] WIP: add tranfer fee extensions --- .../token2022/src/extensions/transfer_fee.rs | 198 +++++++++++++++++- 1 file changed, 189 insertions(+), 9 deletions(-) diff --git a/programs/token2022/src/extensions/transfer_fee.rs b/programs/token2022/src/extensions/transfer_fee.rs index e6bbfb8..be4f33d 100644 --- a/programs/token2022/src/extensions/transfer_fee.rs +++ b/programs/token2022/src/extensions/transfer_fee.rs @@ -1,4 +1,7 @@ -use core::slice::from_raw_parts; +use core::{ + mem::{transmute, MaybeUninit}, + slice::from_raw_parts, +}; use pinocchio::{ account_info::{AccountInfo, Ref}, @@ -11,6 +14,8 @@ use pinocchio::{ use crate::{write_bytes, ID, UNINIT_BYTE}; +pub const MAX_ACCOUNTS_FOR_WITHDRAW: usize = 10; + /// State #[repr(C)] @@ -123,14 +128,14 @@ impl<'a> InitializeTransferFeeConfig<'a> { pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { // Instruction data layout: - // - [0]: instruction discriminator - // - [1..33]: mint - // - [33]: transfer_fee_config_authority_flag - // - [34..66]: transfer_fee_config_authority - // - [66]: withdraw_withheld_authority_flag - // - [67..99]: withdraw_withheld_authority - // - [99..101]: transfer_fee_basis_points - // - [101..109]: maximum_fee + // - [0]: instruction discriminator (1 byte, u8) + // - [1..33]: mint (32 bytes, Pubkey) + // - [33]: transfer_fee_config_authority_flag (1 byte, u8) + // - [34..66]: transfer_fee_config_authority (32 bytes, Pubkey) + // - [66]: withdraw_withheld_authority_flag (1 byte, u8) + // - [67..99]: withdraw_withheld_authority (32 bytes, Pubkey) + // - [99..101]: transfer_fee_basis_points (2 bytes, u16) + // - [101..109]: maximum_fee (8 bytes, u64) let mut instruction_data = [UNINIT_BYTE; 109]; @@ -171,3 +176,178 @@ impl<'a> InitializeTransferFeeConfig<'a> { invoke_signed(&instruction, &[self.mint], signers) } } + +pub struct TransferCheckedWithFee<'a> { + /// Source account + pub source: &'a AccountInfo, + /// Token mint + pub mint: &'a AccountInfo, + /// Destination account + pub destination: &'a AccountInfo, + /// Transfer authority (owner or delegate) + pub authority: &'a AccountInfo, + /// The amount of tokens to transfer. + pub amount: u64, + /// Expected number of base 10 digits to the right of the decimal place. + pub decimals: u8, + /// Expected fee assessed on this transfer, calculated off-chain based + /// on the transfer_fee_basis_points and maximum_fee of the mint. May + /// be 0 for a mint without a configured transfer fee. + pub fee: u64, +} + +impl<'a> TransferCheckedWithFee<'a> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + // Account metadata + let account_metas: [AccountMeta; 4] = [ + AccountMeta::writable(self.source.key()), + AccountMeta::writable(self.mint.key()), + AccountMeta::writable(self.destination.key()), + AccountMeta::readonly_signer(self.authority.key()), + ]; + + // Instruction data layout: + // - [0]: instruction discriminator (1 byte, u8) + // - [1..9]: amount (8 bytes, u64) + // - [9]: decimals (1 byte, u8) + // - [10..18]: fee (8 bytes, u64) + let mut instruction_data = [UNINIT_BYTE; 18]; + + // Set discriminator as u8 at offset [0] + write_bytes(&mut instruction_data, &[28]); + // Set amount as u64 at offset [1..9] + write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes()); + // Set decimals as u8 at offset [9] + write_bytes(&mut instruction_data[9..10], &[self.decimals]); + // Set fee as u64 at offset [10..18] + write_bytes(&mut instruction_data[10..18], &self.fee.to_le_bytes()); + + let instruction = Instruction { + program_id: &crate::ID, + accounts: &account_metas, + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 18) }, + }; + + invoke_signed( + &instruction, + &[self.source, self.mint, self.destination, self.authority], + signers, + ) + } +} + +pub struct WithdrawWithheldTokensFromMint<'a> { + /// Mint account (must include the `TransferFeeConfig` extension) + pub mint: &'a AccountInfo, + /// The fee receiver account (must include the `TransferFeeAmount` extension associated with the provided mint) + pub fee_receiver: &'a AccountInfo, + /// The mint's `withdraw_withheld_authority`. + pub withraw_withheld_authority: &'a AccountInfo, +} + +impl<'a> WithdrawWithheldTokensFromMint<'a> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + // Account metadata + let account_metas: [AccountMeta; 3] = [ + AccountMeta::writable(self.mint.key()), + AccountMeta::writable(self.fee_receiver.key()), + AccountMeta::readonly_signer(self.withraw_withheld_authority.key()), + ]; + + // Instruction data layout: + // - [0]: instruction discriminator + let instruction_data = [29]; + + let instruction = Instruction { + program_id: &crate::ID, + accounts: &account_metas, + data: &instruction_data, + }; + + invoke_signed( + &instruction, + &[ + self.mint, + self.fee_receiver, + self.withraw_withheld_authority, + ], + signers, + ) + } +} + +pub struct WithdrawWithheldTokensFromAccounts<'a> { + /// Mint account (must include the `TransferFeeConfig` extension) + pub mint: &'a AccountInfo, + /// The fee receiver account (must include the `TransferFeeAmount` extension associated with the provided mint) + pub fee_receiver: &'a AccountInfo, + /// The mint's `withdraw_withheld_authority`. + pub withdraw_withheld_authority: &'a AccountInfo, + /// The source accounts to withdraw from. + pub source_accounts: &'a [AccountInfo], +} + +impl<'a> WithdrawWithheldTokensFromAccounts<'a> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + // Account metadata + // let mut account_metas = + // unsafe { MaybeUninit::<[AccountMeta; MAX_ACCOUNTS_FOR_WITHDRAW + 3]>::uninit() }; + + let account_metas = { + let mut data = [const { MaybeUninit::uninit() }; MAX_ACCOUNTS_FOR_WITHDRAW + 3]; + + data[0] = MaybeUninit::new(AccountMeta::writable(self.mint.key())); + + data[1] = MaybeUninit::new(AccountMeta::writable(self.fee_receiver.key())); + data[2] = MaybeUninit::new(AccountMeta::readonly_signer( + self.withdraw_withheld_authority.key(), + )); + + for (i, account) in self.source_accounts.iter().enumerate() { + data[i] = MaybeUninit::new(AccountMeta::writable(account.key())); + } + + unsafe { transmute::<_, [AccountMeta; MAX_ACCOUNTS_FOR_WITHDRAW + 3]>(data) } + }; + // Instruction data layout: + // - [0]: instruction discriminator + let instruction_data = [30]; + + let instruction = Instruction { + program_id: &crate::ID, + accounts: &account_metas, + data: &instruction_data, + }; + + let accounts = { + let mut accounts = [MaybeUninit::uninit(); MAX_ACCOUNTS_FOR_WITHDRAW + 3]; + + accounts[0] = MaybeUninit::new(self.mint); + accounts[1] = MaybeUninit::new(self.fee_receiver); + accounts[2] = MaybeUninit::new(self.withdraw_withheld_authority); + + for (i, account) in self.source_accounts.iter().enumerate() { + accounts[3 + i] = MaybeUninit::new(account); + } + + unsafe { transmute::<_, [&AccountInfo; MAX_ACCOUNTS_FOR_WITHDRAW + 3]>(accounts) } + }; + + invoke_signed(&instruction, &accounts, signers) + } +} From 7f3e0da62c339a058c026db781a2db953905cc9b Mon Sep 17 00:00:00 2001 From: NagaprasadVr Date: Thu, 14 Nov 2024 21:20:37 +0530 Subject: [PATCH 07/13] complete transfer fee extension --- .../token2022/src/extensions/transfer_fee.rs | 199 +++++++++++++++--- 1 file changed, 166 insertions(+), 33 deletions(-) diff --git a/programs/token2022/src/extensions/transfer_fee.rs b/programs/token2022/src/extensions/transfer_fee.rs index be4f33d..48e5ceb 100644 --- a/programs/token2022/src/extensions/transfer_fee.rs +++ b/programs/token2022/src/extensions/transfer_fee.rs @@ -1,7 +1,4 @@ -use core::{ - mem::{transmute, MaybeUninit}, - slice::from_raw_parts, -}; +use core::{mem::MaybeUninit, slice::from_raw_parts}; use pinocchio::{ account_info::{AccountInfo, Ref}, @@ -298,56 +295,192 @@ pub struct WithdrawWithheldTokensFromAccounts<'a> { } impl<'a> WithdrawWithheldTokensFromAccounts<'a> { + /// Invoke `WithdrawWithheldTokensFromAccounts` instruction. + /// this takes a generic const `ACCOUNTS_LEN` to specify the total number of accounts as source accounts length is dynamic. #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) + pub fn invoke(&self) -> ProgramResult { + if 3 + self.source_accounts.len() != ACCOUNTS_LEN { + return Err(ProgramError::Custom(1)); + } + + self.invoke_signed::(&[]) } - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { - // Account metadata - // let mut account_metas = - // unsafe { MaybeUninit::<[AccountMeta; MAX_ACCOUNTS_FOR_WITHDRAW + 3]>::uninit() }; + /// Invoke `WithdrawWithheldTokensFromAccounts` instruction with signers. + /// this takes a generic const `ACCOUNTS_LEN` to specify the total number of accounts as source accounts length is dynamic. + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + if 3 + self.source_accounts.len() != ACCOUNTS_LEN { + return Err(ProgramError::Custom(1)); + } + // Account metads + const UNINIT_ACC_METAS: MaybeUninit = MaybeUninit::::uninit(); + let mut account_metas = [UNINIT_ACC_METAS; ACCOUNTS_LEN]; + + account_metas[0].write(AccountMeta::writable(self.mint.key())); + account_metas[1].write(AccountMeta::writable(self.fee_receiver.key())); + account_metas[2].write(AccountMeta::readonly_signer( + self.withdraw_withheld_authority.key(), + )); + + for (i, account) in self.source_accounts.iter().enumerate() { + account_metas[3 + i].write(AccountMeta::writable(account.key())); + } + + // Instruction data layout: + // - [0]: instruction discriminator + let instruction_data = [30]; + + let acc_metas = unsafe { + core::slice::from_raw_parts(account_metas.as_ptr() as *const AccountMeta, ACCOUNTS_LEN) + }; + + let instruction = Instruction { + program_id: &crate::ID, + accounts: acc_metas, + data: &instruction_data, + }; - let account_metas = { - let mut data = [const { MaybeUninit::uninit() }; MAX_ACCOUNTS_FOR_WITHDRAW + 3]; + const UNINIT_ACC_INFOS: MaybeUninit<&AccountInfo> = MaybeUninit::<&AccountInfo>::uninit(); - data[0] = MaybeUninit::new(AccountMeta::writable(self.mint.key())); + let mut accounts = [UNINIT_ACC_INFOS; ACCOUNTS_LEN]; - data[1] = MaybeUninit::new(AccountMeta::writable(self.fee_receiver.key())); - data[2] = MaybeUninit::new(AccountMeta::readonly_signer( - self.withdraw_withheld_authority.key(), - )); + accounts[0].write(self.mint); + accounts[1].write(self.fee_receiver); + accounts[2].write(self.withdraw_withheld_authority); - for (i, account) in self.source_accounts.iter().enumerate() { - data[i] = MaybeUninit::new(AccountMeta::writable(account.key())); - } + for (i, account) in self.source_accounts.iter().enumerate() { + accounts[3 + i].write(account); + } - unsafe { transmute::<_, [AccountMeta; MAX_ACCOUNTS_FOR_WITHDRAW + 3]>(data) } + let acc_infos: [&AccountInfo; ACCOUNTS_LEN] = unsafe { + core::slice::from_raw_parts(accounts.as_ptr() as *const &AccountInfo, ACCOUNTS_LEN) + .try_into() + .unwrap() // this is safe as we know the length of the array }; + + invoke_signed(&instruction, &acc_infos, signers) + } +} + +pub struct HarvestWithheldTokensToMint<'a> { + /// Mint account (must include the `TransferFeeConfig` extension) + mint: &'a AccountInfo, + /// The source accounts to harvest from. + source_accounts: &'a [AccountInfo], +} + +impl<'a> HarvestWithheldTokensToMint<'a> { + /// Invoke `HarvestWithheldTokensToMint` instruction. + /// this takes a generic const `ACCOUNTS_LEN` to specify the total number of accounts as source accounts length is dynamic. + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + if 1 + self.source_accounts.len() != ACCOUNTS_LEN { + return Err(ProgramError::Custom(1)); + } + self.invoke_signed::(&[]) + } + + /// Invoke `HarvestWithheldTokensToMint` instruction with signers. + /// this takes a generic const `ACCOUNTS_LEN` to specify the total number of accounts as source accounts length is dynamic. + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + if 1 + self.source_accounts.len() != ACCOUNTS_LEN { + return Err(ProgramError::Custom(1)); + } + + // Account metads + const UNINIT_ACC_METAS: MaybeUninit = MaybeUninit::::uninit(); + let mut account_metas = [UNINIT_ACC_METAS; ACCOUNTS_LEN]; + + account_metas[0].write(AccountMeta::writable(self.mint.key())); + + for (i, account) in self.source_accounts.iter().enumerate() { + account_metas[1 + i].write(AccountMeta::writable(account.key())); + } + // Instruction data layout: // - [0]: instruction discriminator - let instruction_data = [30]; + let instruction_data = [31]; + + let acc_metas = unsafe { + core::slice::from_raw_parts(account_metas.as_ptr() as *const AccountMeta, ACCOUNTS_LEN) + }; let instruction = Instruction { program_id: &crate::ID, - accounts: &account_metas, + accounts: acc_metas, data: &instruction_data, }; - let accounts = { - let mut accounts = [MaybeUninit::uninit(); MAX_ACCOUNTS_FOR_WITHDRAW + 3]; + const UNINIT_ACC_INFOS: MaybeUninit<&AccountInfo> = MaybeUninit::<&AccountInfo>::uninit(); + + let mut accounts = [UNINIT_ACC_INFOS; ACCOUNTS_LEN]; - accounts[0] = MaybeUninit::new(self.mint); - accounts[1] = MaybeUninit::new(self.fee_receiver); - accounts[2] = MaybeUninit::new(self.withdraw_withheld_authority); + accounts[0].write(self.mint); - for (i, account) in self.source_accounts.iter().enumerate() { - accounts[3 + i] = MaybeUninit::new(account); - } + for (i, account) in self.source_accounts.iter().enumerate() { + accounts[1 + i].write(account); + } + + let acc_infos: [&AccountInfo; ACCOUNTS_LEN] = unsafe { + core::slice::from_raw_parts(accounts.as_ptr() as *const &AccountInfo, ACCOUNTS_LEN) + .try_into() + .unwrap() // this is safe as we know the length of the array + }; + + invoke_signed(&instruction, &acc_infos, signers) + } +} + +pub struct SetTransferFee<'a> { + /// Mint account + pub mint: &'a AccountInfo, + /// The mint's fee account owner. + pub mint_fee_acc_owner: &'a AccountInfo, + /// Amount of transfer collected as fees, expressed as basis points of + /// the transfer amount + pub transfer_fee_basis_points: u16, + /// Maximum fee assessed on transfers + pub maximum_fee: u64, +} - unsafe { transmute::<_, [&AccountInfo; MAX_ACCOUNTS_FOR_WITHDRAW + 3]>(accounts) } +impl<'a> SetTransferFee<'a> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + // Account metadata + let account_metas: [AccountMeta; 2] = [ + AccountMeta::writable(self.mint.key()), + AccountMeta::readonly(self.mint_fee_acc_owner.key()), + ]; + + // Instruction data layout: + // - [0]: instruction discriminator (1 byte, u8) + // - [1..3]: transfer_fee_basis_points (2 bytes, u16) + // - [3..11]: maximum_fee (8 bytes, u64) + let mut instruction_data = [UNINIT_BYTE; 11]; + + // Set discriminator as u8 at offset [0] + write_bytes(&mut instruction_data, &[32]); + // Set transfer_fee_basis_points as u16 at offset [1..3] + write_bytes( + &mut instruction_data[1..3], + &self.transfer_fee_basis_points.to_le_bytes(), + ); + // Set maximum_fee as u64 at offset [3..11] + write_bytes( + &mut instruction_data[3..11], + &self.maximum_fee.to_le_bytes(), + ); + + let instruction = Instruction { + program_id: &crate::ID, + accounts: &account_metas, + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 11) }, }; - invoke_signed(&instruction, &accounts, signers) + invoke_signed(&instruction, &[self.mint, self.mint_fee_acc_owner], signers) } } From 857610f37b769e1873b29a65ef59556c04c9ff35 Mon Sep 17 00:00:00 2001 From: NagaprasadVr Date: Fri, 15 Nov 2024 10:11:45 +0530 Subject: [PATCH 08/13] chore:cleanup --- programs/token/src/lib.rs | 5 ++ .../token2022/src/extensions/transfer_fee.rs | 48 ++++++++++--------- programs/token2022/src/lib.rs | 5 ++ 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/programs/token/src/lib.rs b/programs/token/src/lib.rs index 6ad2eb9..ce23065 100644 --- a/programs/token/src/lib.rs +++ b/programs/token/src/lib.rs @@ -9,6 +9,11 @@ use core::mem::MaybeUninit; const UNINIT_BYTE: MaybeUninit = MaybeUninit::::uninit(); +/// Deserialize a type from a byte array. +/// +/// # Safety +/// +/// This function is unsafe because it transmutes the input data to the output type. pub unsafe fn from_bytes(data: &[u8]) -> T { assert_eq!(data.len(), core::mem::size_of::()); *(data.as_ptr() as *const T) diff --git a/programs/token2022/src/extensions/transfer_fee.rs b/programs/token2022/src/extensions/transfer_fee.rs index 48e5ceb..13ea879 100644 --- a/programs/token2022/src/extensions/transfer_fee.rs +++ b/programs/token2022/src/extensions/transfer_fee.rs @@ -13,8 +13,7 @@ use crate::{write_bytes, ID, UNINIT_BYTE}; pub const MAX_ACCOUNTS_FOR_WITHDRAW: usize = 10; -/// State - +/// Transfer fee configuration #[repr(C)] pub struct TransferFee { /// First epoch where the transfer fee takes effect @@ -26,6 +25,8 @@ pub struct TransferFee { pub transfer_fee_basis_points: [u8; 8], } +/// State + #[repr(C)] pub struct TransferFeeConfig { /// flag to indicate if the transfer fee config authority is present @@ -101,8 +102,9 @@ impl TransferFeeConfig { } } -/// Instruction +/// Instructions +/// Initialize the transfer fee configuration for a mint. pub struct InitializeTransferFeeConfig<'a> { // Mint account pub mint: &'a AccountInfo, @@ -174,6 +176,8 @@ impl<'a> InitializeTransferFeeConfig<'a> { } } +/// Transfer tokens from one account to another, with a fee. + pub struct TransferCheckedWithFee<'a> { /// Source account pub source: &'a AccountInfo, @@ -238,6 +242,8 @@ impl<'a> TransferCheckedWithFee<'a> { } } +/// Withdraw withheld tokens from the mint account. + pub struct WithdrawWithheldTokensFromMint<'a> { /// Mint account (must include the `TransferFeeConfig` extension) pub mint: &'a AccountInfo, @@ -283,7 +289,9 @@ impl<'a> WithdrawWithheldTokensFromMint<'a> { } } -pub struct WithdrawWithheldTokensFromAccounts<'a> { +/// Withdraw withheld tokens from the provided source accounts. + +pub struct WithdrawWithheldTokensFromAccounts<'a, const ACCOUNTS_LEN: usize> { /// Mint account (must include the `TransferFeeConfig` extension) pub mint: &'a AccountInfo, /// The fee receiver account (must include the `TransferFeeAmount` extension associated with the provided mint) @@ -291,24 +299,20 @@ pub struct WithdrawWithheldTokensFromAccounts<'a> { /// The mint's `withdraw_withheld_authority`. pub withdraw_withheld_authority: &'a AccountInfo, /// The source accounts to withdraw from. - pub source_accounts: &'a [AccountInfo], + pub source_accounts: &'a [&'a AccountInfo], } -impl<'a> WithdrawWithheldTokensFromAccounts<'a> { - /// Invoke `WithdrawWithheldTokensFromAccounts` instruction. - /// this takes a generic const `ACCOUNTS_LEN` to specify the total number of accounts as source accounts length is dynamic. +impl<'a, const ACCOUNTS_LEN: usize> WithdrawWithheldTokensFromAccounts<'a, ACCOUNTS_LEN> { #[inline(always)] - pub fn invoke(&self) -> ProgramResult { + pub fn invoke(&self) -> ProgramResult { if 3 + self.source_accounts.len() != ACCOUNTS_LEN { return Err(ProgramError::Custom(1)); } - self.invoke_signed::(&[]) + self.invoke_signed(&[]) } - /// Invoke `WithdrawWithheldTokensFromAccounts` instruction with signers. - /// this takes a generic const `ACCOUNTS_LEN` to specify the total number of accounts as source accounts length is dynamic. - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { if 3 + self.source_accounts.len() != ACCOUNTS_LEN { return Err(ProgramError::Custom(1)); } @@ -362,27 +366,24 @@ impl<'a> WithdrawWithheldTokensFromAccounts<'a> { } } -pub struct HarvestWithheldTokensToMint<'a> { +/// Harvest withheld tokens to mint accounts. +pub struct HarvestWithheldTokensToMint<'a, const ACCOUNTS_LEN: usize> { /// Mint account (must include the `TransferFeeConfig` extension) mint: &'a AccountInfo, /// The source accounts to harvest from. - source_accounts: &'a [AccountInfo], + source_accounts: &'a [&'a AccountInfo], } -impl<'a> HarvestWithheldTokensToMint<'a> { - /// Invoke `HarvestWithheldTokensToMint` instruction. - /// this takes a generic const `ACCOUNTS_LEN` to specify the total number of accounts as source accounts length is dynamic. +impl<'a, const ACCOUNTS_LEN: usize> HarvestWithheldTokensToMint<'a, ACCOUNTS_LEN> { #[inline(always)] - pub fn invoke(&self) -> ProgramResult { + pub fn invoke(&self) -> ProgramResult { if 1 + self.source_accounts.len() != ACCOUNTS_LEN { return Err(ProgramError::Custom(1)); } - self.invoke_signed::(&[]) + self.invoke_signed(&[]) } - /// Invoke `HarvestWithheldTokensToMint` instruction with signers. - /// this takes a generic const `ACCOUNTS_LEN` to specify the total number of accounts as source accounts length is dynamic. - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { if 1 + self.source_accounts.len() != ACCOUNTS_LEN { return Err(ProgramError::Custom(1)); } @@ -431,6 +432,7 @@ impl<'a> HarvestWithheldTokensToMint<'a> { } } +/// Set the transfer fee configuration for a mint. pub struct SetTransferFee<'a> { /// Mint account pub mint: &'a AccountInfo, diff --git a/programs/token2022/src/lib.rs b/programs/token2022/src/lib.rs index a2d04ef..01b8d20 100644 --- a/programs/token2022/src/lib.rs +++ b/programs/token2022/src/lib.rs @@ -10,6 +10,11 @@ use core::mem::MaybeUninit; const UNINIT_BYTE: MaybeUninit = MaybeUninit::::uninit(); +/// Deserialize a type from a byte array. +/// +/// # Safety +/// +/// This function is unsafe because it transmutes the input data to the output type. pub unsafe fn from_bytes(data: &[u8]) -> T { assert_eq!(data.len(), core::mem::size_of::()); *(data.as_ptr() as *const T) From 58c0c6046c04d77e4f74c0baff91779e8bb35fbc Mon Sep 17 00:00:00 2001 From: NagaprasadVr Date: Fri, 15 Nov 2024 10:22:11 +0530 Subject: [PATCH 09/13] rm common ixs until extensions are done --- .../token2022/src/instructions/approve.rs | 65 --------------- .../src/instructions/approve_checked.rs | 74 ----------------- programs/token2022/src/instructions/burn.rs | 65 --------------- .../src/instructions/burn_checked.rs | 69 ---------------- .../src/instructions/close_account.rs | 49 ------------ .../src/instructions/freeze_account.rs | 49 ------------ .../src/instructions/initialize_account.rs | 53 ------------ .../src/instructions/initialize_account_2.rs | 66 --------------- .../src/instructions/initialize_account_3.rs | 58 -------------- .../src/instructions/initialize_mint.rs | 74 ----------------- .../src/instructions/initialize_mint_2.rs | 68 ---------------- .../token2022/src/instructions/mint_to.rs | 66 --------------- .../src/instructions/mint_to_checked.rs | 71 ---------------- programs/token2022/src/instructions/mod.rs | 39 --------- programs/token2022/src/instructions/revoke.rs | 41 ---------- .../src/instructions/set_authority.rs | 80 ------------------- .../token2022/src/instructions/sync_native.rs | 37 --------- .../src/instructions/thaw_account.rs | 49 ------------ .../token2022/src/instructions/transfer.rs | 61 -------------- .../src/instructions/transfer_checked.rs | 70 ---------------- programs/token2022/src/lib.rs | 1 - 21 files changed, 1205 deletions(-) delete mode 100644 programs/token2022/src/instructions/approve.rs delete mode 100644 programs/token2022/src/instructions/approve_checked.rs delete mode 100644 programs/token2022/src/instructions/burn.rs delete mode 100644 programs/token2022/src/instructions/burn_checked.rs delete mode 100644 programs/token2022/src/instructions/close_account.rs delete mode 100644 programs/token2022/src/instructions/freeze_account.rs delete mode 100644 programs/token2022/src/instructions/initialize_account.rs delete mode 100644 programs/token2022/src/instructions/initialize_account_2.rs delete mode 100644 programs/token2022/src/instructions/initialize_account_3.rs delete mode 100644 programs/token2022/src/instructions/initialize_mint.rs delete mode 100644 programs/token2022/src/instructions/initialize_mint_2.rs delete mode 100644 programs/token2022/src/instructions/mint_to.rs delete mode 100644 programs/token2022/src/instructions/mint_to_checked.rs delete mode 100644 programs/token2022/src/instructions/mod.rs delete mode 100644 programs/token2022/src/instructions/revoke.rs delete mode 100644 programs/token2022/src/instructions/set_authority.rs delete mode 100644 programs/token2022/src/instructions/sync_native.rs delete mode 100644 programs/token2022/src/instructions/thaw_account.rs delete mode 100644 programs/token2022/src/instructions/transfer.rs delete mode 100644 programs/token2022/src/instructions/transfer_checked.rs diff --git a/programs/token2022/src/instructions/approve.rs b/programs/token2022/src/instructions/approve.rs deleted file mode 100644 index d9596f7..0000000 --- a/programs/token2022/src/instructions/approve.rs +++ /dev/null @@ -1,65 +0,0 @@ -use core::slice::from_raw_parts; - -use pinocchio::{ - account_info::AccountInfo, - instruction::{AccountMeta, Instruction, Signer}, - program::invoke_signed, - ProgramResult, -}; - -use crate::{write_bytes, UNINIT_BYTE}; - -/// Approves a delegate. -/// -/// ### Accounts: -/// 0. `[WRITE]` The token account. -/// 1. `[]` The delegate. -/// 2. `[SIGNER]` The source account owner. -pub struct Approve<'a> { - /// Source Account. - pub token: &'a AccountInfo, - /// Delegate Account - pub delegate: &'a AccountInfo, - /// Source Owner Account - pub authority: &'a AccountInfo, - /// Amount - pub amount: u64, -} - -impl<'a> Approve<'a> { - #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) - } - - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { - // Account metadata - let account_metas: [AccountMeta; 3] = [ - AccountMeta::writable(self.token.key()), - AccountMeta::readonly(self.delegate.key()), - AccountMeta::readonly_signer(self.authority.key()), - ]; - - // Instruction data - // - [0]: instruction discriminator (1 byte, u8) - // - [1..9]: amount (8 bytes, u64) - let mut instruction_data = [UNINIT_BYTE; 9]; - - // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[4]); - // Set amount as u64 at offset [1..9] - write_bytes(&mut instruction_data[1..], &self.amount.to_le_bytes()); - - let instruction = Instruction { - program_id: &crate::ID, - accounts: &account_metas, - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 9) }, - }; - - invoke_signed( - &instruction, - &[self.token, self.delegate, self.authority], - signers, - ) - } -} diff --git a/programs/token2022/src/instructions/approve_checked.rs b/programs/token2022/src/instructions/approve_checked.rs deleted file mode 100644 index 6195122..0000000 --- a/programs/token2022/src/instructions/approve_checked.rs +++ /dev/null @@ -1,74 +0,0 @@ -use core::slice::from_raw_parts; - -use pinocchio::{ - account_info::AccountInfo, - instruction::{AccountMeta, Instruction, Signer}, - program::invoke_signed, - ProgramResult, -}; - -use crate::{write_bytes, UNINIT_BYTE}; - -/// Approves a delegate. -/// -/// ### Accounts: -/// 0. `[WRITE]` The source account. -/// 1. `[]` The token mint. -/// 2. `[]` The delegate. -/// 3. `[SIGNER]` The source account owner. -pub struct ApproveChecked<'a> { - /// Source Account. - pub token: &'a AccountInfo, - /// Mint Account. - pub mint: &'a AccountInfo, - /// Delegate Account. - pub delegate: &'a AccountInfo, - /// Source Owner Account. - pub authority: &'a AccountInfo, - /// Amount. - pub amount: u64, - /// Decimals. - pub decimals: u8, -} - -impl<'a> ApproveChecked<'a> { - #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) - } - - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { - // Account metadata - let account_metas: [AccountMeta; 4] = [ - AccountMeta::writable(self.token.key()), - AccountMeta::readonly(self.mint.key()), - AccountMeta::readonly(self.delegate.key()), - AccountMeta::readonly_signer(self.authority.key()), - ]; - - // Instruction data - // - [0] : instruction discriminator (1 byte, u8) - // - [1..9]: amount (8 bytes, u64) - // - [9] : decimals (1 byte, u8) - let mut instruction_data = [UNINIT_BYTE; 10]; - - // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[13]); - // Set amount as u64 at offset [1..9] - write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes()); - // Set decimals as u8 at offset [9] - write_bytes(&mut instruction_data[9..], &[self.decimals]); - - let instruction = Instruction { - program_id: &crate::ID, - accounts: &account_metas, - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 10) }, - }; - - invoke_signed( - &instruction, - &[self.token, self.mint, self.delegate, self.authority], - signers, - ) - } -} diff --git a/programs/token2022/src/instructions/burn.rs b/programs/token2022/src/instructions/burn.rs deleted file mode 100644 index 4a8e71a..0000000 --- a/programs/token2022/src/instructions/burn.rs +++ /dev/null @@ -1,65 +0,0 @@ -use core::slice::from_raw_parts; - -use pinocchio::{ - account_info::AccountInfo, - instruction::{AccountMeta, Instruction, Signer}, - program::invoke_signed, - ProgramResult, -}; - -use crate::{write_bytes, UNINIT_BYTE}; - -/// Burns tokens by removing them from an account. -/// -/// ### Accounts: -/// 0. `[WRITE]` The account to burn from. -/// 1. `[WRITE]` The token mint. -/// 2. `[SIGNER]` The account's owner/delegate. -pub struct Burn<'a> { - /// Source of the Burn Account - pub token: &'a AccountInfo, - /// Mint Account - pub mint: &'a AccountInfo, - /// Owner of the Token Account - pub authority: &'a AccountInfo, - /// Amount - pub amount: u64, -} - -impl<'a> Burn<'a> { - #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) - } - - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { - // Account metadata - let account_metas: [AccountMeta; 3] = [ - AccountMeta::writable(self.token.key()), - AccountMeta::writable(self.mint.key()), - AccountMeta::readonly_signer(self.authority.key()), - ]; - - // Instruction data - // - [0]: instruction discriminator (1 byte, u8) - // - [1..9]: amount (8 bytes, u64) - let mut instruction_data = [UNINIT_BYTE; 9]; - - // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[8]); - // Set amount as u64 at offset [1..9] - write_bytes(&mut instruction_data[1..], &self.amount.to_le_bytes()); - - let instruction = Instruction { - program_id: &crate::ID, - accounts: &account_metas, - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 9) }, - }; - - invoke_signed( - &instruction, - &[self.token, self.mint, self.authority], - signers, - ) - } -} diff --git a/programs/token2022/src/instructions/burn_checked.rs b/programs/token2022/src/instructions/burn_checked.rs deleted file mode 100644 index 789a320..0000000 --- a/programs/token2022/src/instructions/burn_checked.rs +++ /dev/null @@ -1,69 +0,0 @@ -use core::slice::from_raw_parts; - -use crate::{write_bytes, UNINIT_BYTE}; -use pinocchio::{ - account_info::AccountInfo, - instruction::{AccountMeta, Instruction, Signer}, - program::invoke_signed, - ProgramResult, -}; - -/// Burns tokens by removing them from an account. -/// -/// ### Accounts: -/// 0. `[WRITE]` The account to burn from. -/// 1. `[WRITE]` The token mint. -/// 2. `[SIGNER]` The account's owner/delegate. -pub struct BurnChecked<'a> { - /// Source of the Burn Account - pub token: &'a AccountInfo, - /// Mint Account - pub mint: &'a AccountInfo, - /// Owner of the Token Account - pub authority: &'a AccountInfo, - /// Amount - pub amount: u64, - /// Decimals - pub decimals: u8, -} - -impl<'a> BurnChecked<'a> { - #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) - } - - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { - // Account metadata - let account_metas: [AccountMeta; 3] = [ - AccountMeta::writable(self.token.key()), - AccountMeta::writable(self.mint.key()), - AccountMeta::readonly_signer(self.authority.key()), - ]; - - // Instruction data - // - [0]: instruction discriminator (1 byte, u8) - // - [1..9]: amount (8 bytes, u64) - // - [9]: decimals (1 byte, u8) - let mut instruction_data = [UNINIT_BYTE; 10]; - - // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[15]); - // Set amount as u64 at offset [1..9] - write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes()); - // Set decimals as u8 at offset [9] - write_bytes(&mut instruction_data[9..], &[self.decimals]); - - let instruction = Instruction { - program_id: &crate::ID, - accounts: &account_metas, - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 10) }, - }; - - invoke_signed( - &instruction, - &[self.token, self.mint, self.authority], - signers, - ) - } -} diff --git a/programs/token2022/src/instructions/close_account.rs b/programs/token2022/src/instructions/close_account.rs deleted file mode 100644 index 19461d9..0000000 --- a/programs/token2022/src/instructions/close_account.rs +++ /dev/null @@ -1,49 +0,0 @@ -use pinocchio::{ - account_info::AccountInfo, - instruction::{AccountMeta, Instruction, Signer}, - program::invoke_signed, - ProgramResult, -}; - -/// Close an account by transferring all its SOL to the destination account. -/// -/// ### Accounts: -/// 0. `[WRITE]` The account to close. -/// 1. `[WRITE]` The destination account. -/// 2. `[SIGNER]` The account's owner. -pub struct CloseAccount<'a> { - /// Token Account. - pub account: &'a AccountInfo, - /// Destination Account - pub destination: &'a AccountInfo, - /// Owner Account - pub authority: &'a AccountInfo, -} - -impl<'a> CloseAccount<'a> { - #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) - } - - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { - // account metadata - let account_metas: [AccountMeta; 3] = [ - AccountMeta::writable(self.account.key()), - AccountMeta::writable(self.destination.key()), - AccountMeta::readonly_signer(self.authority.key()), - ]; - - let instruction = Instruction { - program_id: &crate::ID, - accounts: &account_metas, - data: &[9], - }; - - invoke_signed( - &instruction, - &[self.account, self.destination, self.authority], - signers, - ) - } -} diff --git a/programs/token2022/src/instructions/freeze_account.rs b/programs/token2022/src/instructions/freeze_account.rs deleted file mode 100644 index a2c9c02..0000000 --- a/programs/token2022/src/instructions/freeze_account.rs +++ /dev/null @@ -1,49 +0,0 @@ -use pinocchio::{ - account_info::AccountInfo, - instruction::{AccountMeta, Instruction, Signer}, - program::invoke_signed, - ProgramResult, -}; - -/// Freeze an Initialized account using the Mint's freeze_authority -/// -/// ### Accounts: -/// 0. `[WRITE]` The account to freeze. -/// 1. `[]` The token mint. -/// 2. `[SIGNER]` The mint freeze authority. -pub struct FreezeAccount<'a> { - /// Token Account to freeze. - pub token: &'a AccountInfo, - /// Mint Account. - pub mint: &'a AccountInfo, - /// Mint Freeze Authority Account - pub freeze_authority: &'a AccountInfo, -} - -impl<'a> FreezeAccount<'a> { - #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) - } - - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { - // account metadata - let account_metas: [AccountMeta; 3] = [ - AccountMeta::writable(self.token.key()), - AccountMeta::readonly(self.mint.key()), - AccountMeta::readonly_signer(self.freeze_authority.key()), - ]; - - let instruction = Instruction { - program_id: &crate::ID, - accounts: &account_metas, - data: &[10], - }; - - invoke_signed( - &instruction, - &[self.token, self.mint, self.freeze_authority], - signers, - ) - } -} diff --git a/programs/token2022/src/instructions/initialize_account.rs b/programs/token2022/src/instructions/initialize_account.rs deleted file mode 100644 index 5d5603d..0000000 --- a/programs/token2022/src/instructions/initialize_account.rs +++ /dev/null @@ -1,53 +0,0 @@ -use pinocchio::{ - account_info::AccountInfo, - instruction::{AccountMeta, Instruction, Signer}, - program::invoke_signed, - ProgramResult, -}; - -/// Initialize a new Token Account. -/// -/// ### Accounts: -/// 0. `[WRITE]` The account to initialize. -/// 1. `[]` The mint this account will be associated with. -/// 2. `[]` The new account's owner/multisignature. -/// 3. `[]` Rent sysvar -pub struct InitilizeAccount<'a> { - /// New Account. - pub token: &'a AccountInfo, - /// Mint Account. - pub mint: &'a AccountInfo, - /// Owner of the new Account. - pub owner: &'a AccountInfo, - /// Rent Sysvar Account - pub rent_sysvar: &'a AccountInfo, -} - -impl<'a> InitilizeAccount<'a> { - #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) - } - - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { - // account metadata - let account_metas: [AccountMeta; 4] = [ - AccountMeta::writable(self.token.key()), - AccountMeta::readonly(self.mint.key()), - AccountMeta::readonly(self.owner.key()), - AccountMeta::readonly(self.rent_sysvar.key()), - ]; - - let instruction = Instruction { - program_id: &crate::ID, - accounts: &account_metas, - data: &[1], - }; - - invoke_signed( - &instruction, - &[self.token, self.mint, self.owner, self.rent_sysvar], - signers, - ) - } -} diff --git a/programs/token2022/src/instructions/initialize_account_2.rs b/programs/token2022/src/instructions/initialize_account_2.rs deleted file mode 100644 index 430d9e9..0000000 --- a/programs/token2022/src/instructions/initialize_account_2.rs +++ /dev/null @@ -1,66 +0,0 @@ -use core::slice::from_raw_parts; - -use pinocchio::{ - account_info::AccountInfo, - instruction::{AccountMeta, Instruction, Signer}, - program::invoke_signed, - pubkey::Pubkey, - ProgramResult, -}; - -use crate::{write_bytes, UNINIT_BYTE}; - -/// Initialize a new Token Account. -/// -/// ### Accounts: -/// 0. `[WRITE]` The account to initialize. -/// 1. `[]` The mint this account will be associated with. -/// 3. `[]` Rent sysvar -pub struct InitilizeAccount2<'a> { - /// New Account. - pub token: &'a AccountInfo, - /// Mint Account. - pub mint: &'a AccountInfo, - /// Rent Sysvar Account - pub rent_sysvar: &'a AccountInfo, - /// Owner of the new Account. - pub owner: &'a Pubkey, -} - -impl<'a> InitilizeAccount2<'a> { - #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) - } - - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { - // account metadata - let account_metas: [AccountMeta; 3] = [ - AccountMeta::writable(self.token.key()), - AccountMeta::readonly(self.mint.key()), - AccountMeta::readonly(self.rent_sysvar.key()), - ]; - - // instruction data - // - [0]: instruction discriminator - // - [1..33]: owner - let mut instruction_data = [UNINIT_BYTE; 33]; - - // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[16]); - // Set owner as [u8; 32] at offset [1..33] - write_bytes(&mut instruction_data[1..], self.owner); - - let instruction = Instruction { - program_id: &crate::ID, - accounts: &account_metas, - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 33) }, - }; - - invoke_signed( - &instruction, - &[self.token, self.mint, self.rent_sysvar], - signers, - ) - } -} diff --git a/programs/token2022/src/instructions/initialize_account_3.rs b/programs/token2022/src/instructions/initialize_account_3.rs deleted file mode 100644 index 6a573c5..0000000 --- a/programs/token2022/src/instructions/initialize_account_3.rs +++ /dev/null @@ -1,58 +0,0 @@ -use core::slice::from_raw_parts; - -use pinocchio::{ - account_info::AccountInfo, - instruction::{AccountMeta, Instruction, Signer}, - program::invoke_signed, - pubkey::Pubkey, - ProgramResult, -}; - -use crate::{write_bytes, UNINIT_BYTE}; - -/// Initialize a new Token Account. -/// -/// ### Accounts: -/// 0. `[WRITE]` The account to initialize. -/// 1. `[]` The mint this account will be associated with. -pub struct InitilizeAccount3<'a> { - /// New Account. - pub token: &'a AccountInfo, - /// Mint Account. - pub mint: &'a AccountInfo, - /// Owner of the new Account. - pub owner: &'a Pubkey, -} - -impl<'a> InitilizeAccount3<'a> { - #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) - } - - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { - // account metadata - let account_metas: [AccountMeta; 2] = [ - AccountMeta::writable(self.token.key()), - AccountMeta::readonly(self.mint.key()), - ]; - - // instruction data - // - [0]: instruction discriminator - // - [1..33]: owner - let mut instruction_data = [UNINIT_BYTE; 33]; - - // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[18]); - // Set owner as [u8; 32] at offset [1..33] - write_bytes(&mut instruction_data[1..], self.owner); - - let instruction = Instruction { - program_id: &crate::ID, - accounts: &account_metas, - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 33) }, - }; - - invoke_signed(&instruction, &[self.token, self.mint], signers) - } -} diff --git a/programs/token2022/src/instructions/initialize_mint.rs b/programs/token2022/src/instructions/initialize_mint.rs deleted file mode 100644 index 1e91c89..0000000 --- a/programs/token2022/src/instructions/initialize_mint.rs +++ /dev/null @@ -1,74 +0,0 @@ -use core::slice::from_raw_parts; - -use pinocchio::{ - account_info::AccountInfo, - instruction::{AccountMeta, Instruction, Signer}, - program::invoke_signed, - pubkey::Pubkey, - ProgramResult, -}; - -use crate::{write_bytes, UNINIT_BYTE}; - -/// Initialize a new mint. -/// -/// ### Accounts: -/// 0. `[WRITABLE]` Mint account -/// 1. `[]` Rent sysvar -pub struct InitilizeMint<'a> { - /// Mint Account. - pub mint: &'a AccountInfo, - /// Rent sysvar Account. - pub rent_sysvar: &'a AccountInfo, - /// Decimals. - pub decimals: u8, - /// Mint Authority. - pub mint_authority: &'a Pubkey, - /// Freeze Authority. - pub freeze_authority: Option<&'a Pubkey>, -} - -impl<'a> InitilizeMint<'a> { - #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) - } - - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { - // Account metadata - let account_metas: [AccountMeta; 2] = [ - AccountMeta::writable(self.mint.key()), - AccountMeta::readonly(self.rent_sysvar.key()), - ]; - - // Instruction data layout: - // - [0]: instruction discriminator - // - [1]: decimals - // - [2..34]: mint_authority - // - [34]: freeze_authority presence flag - // - [35..68]: freeze_authority - let mut instruction_data = [UNINIT_BYTE; 67]; - - // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[0]); - // Set decimals as u8 at offset [1] - write_bytes(&mut instruction_data[1..2], &[self.decimals]); - // Set mint_authority as Pubkey at offset [2..34] - write_bytes(&mut instruction_data[2..34], self.mint_authority); - // Set COption & freeze_authority at offset [34..67] - if let Some(freeze_auth) = self.freeze_authority { - write_bytes(&mut instruction_data[34..35], &[1]); - write_bytes(&mut instruction_data[35..], freeze_auth); - } else { - write_bytes(&mut instruction_data[34..35], &[0]); - } - - let instruction = Instruction { - program_id: &crate::ID, - accounts: &account_metas, - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 67) }, - }; - - invoke_signed(&instruction, &[self.mint, self.rent_sysvar], signers) - } -} diff --git a/programs/token2022/src/instructions/initialize_mint_2.rs b/programs/token2022/src/instructions/initialize_mint_2.rs deleted file mode 100644 index 6a53ca5..0000000 --- a/programs/token2022/src/instructions/initialize_mint_2.rs +++ /dev/null @@ -1,68 +0,0 @@ -use core::slice::from_raw_parts; - -use pinocchio::{ - account_info::AccountInfo, - instruction::{AccountMeta, Instruction, Signer}, - program::invoke_signed, - pubkey::Pubkey, - ProgramResult, -}; - -use crate::{write_bytes, UNINIT_BYTE}; - -/// Initialize a new mint. -/// -/// ### Accounts: -/// 0. `[WRITABLE]` Mint account -pub struct InitilizeMint2<'a> { - /// Mint Account. - pub mint: &'a AccountInfo, - /// Decimals. - pub decimals: u8, - /// Mint Authority. - pub mint_authority: &'a Pubkey, - /// Freeze Authority. - pub freeze_authority: Option<&'a Pubkey>, -} - -impl<'a> InitilizeMint2<'a> { - #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) - } - - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { - // Account metadata - let account_metas: [AccountMeta; 1] = [AccountMeta::writable(self.mint.key())]; - - // Instruction data layout: - // - [0]: instruction discriminator - // - [1]: decimals - // - [2..34]: mint_authority - // - [34..35]: freeze_authority presence flag - // - [35..67]: freeze_authority - let mut instruction_data = [UNINIT_BYTE; 67]; - - // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[20]); - // Set decimals as u8 at offset [1] - write_bytes(&mut instruction_data[1..2], &[self.decimals]); - // Set mint_authority as Pubkey at offset [2..34] - write_bytes(&mut instruction_data[2..34], self.mint_authority); - // Set COption & freeze_authority at offset [34..67] - if let Some(freeze_auth) = self.freeze_authority { - write_bytes(&mut instruction_data[34..35], &[1]); - write_bytes(&mut instruction_data[35..], freeze_auth); - } else { - write_bytes(&mut instruction_data[34..35], &[0]); - } - - let instruction = Instruction { - program_id: &crate::ID, - accounts: &account_metas, - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 67) }, - }; - - invoke_signed(&instruction, &[self.mint], signers) - } -} diff --git a/programs/token2022/src/instructions/mint_to.rs b/programs/token2022/src/instructions/mint_to.rs deleted file mode 100644 index f0da1d4..0000000 --- a/programs/token2022/src/instructions/mint_to.rs +++ /dev/null @@ -1,66 +0,0 @@ -use core::slice::from_raw_parts; - -use pinocchio::{ - account_info::AccountInfo, - instruction::{AccountMeta, Instruction, Signer}, - program::invoke_signed, - ProgramResult, -}; - -use crate::{write_bytes, UNINIT_BYTE}; - -/// Mints new tokens to an account. -/// -/// ### Accounts: -/// 0. `[WRITE]` The mint. -/// 1. `[WRITE]` The account to mint tokens to. -/// 2. `[SIGNER]` The mint's minting authority. -/// -pub struct MintTo<'a> { - /// Mint Account. - pub mint: &'a AccountInfo, - /// Token Account. - pub token: &'a AccountInfo, - /// Mint Authority - pub mint_authority: &'a AccountInfo, - /// Amount - pub amount: u64, -} - -impl<'a> MintTo<'a> { - #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) - } - - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { - // account metadata - let account_metas: [AccountMeta; 3] = [ - AccountMeta::writable(self.mint.key()), - AccountMeta::writable(self.token.key()), - AccountMeta::readonly_signer(self.mint_authority.key()), - ]; - - // Instruction data layout: - // - [0]: instruction discriminator - // - [1..9]: amount - let mut instruction_data = [UNINIT_BYTE; 9]; - - // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[7]); - // Set amount as u64 at offset [1..9] - write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes()); - - let instruction = Instruction { - program_id: &crate::ID, - accounts: &account_metas, - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 9) }, - }; - - invoke_signed( - &instruction, - &[self.mint, self.token, self.mint_authority], - signers, - ) - } -} diff --git a/programs/token2022/src/instructions/mint_to_checked.rs b/programs/token2022/src/instructions/mint_to_checked.rs deleted file mode 100644 index 67d0608..0000000 --- a/programs/token2022/src/instructions/mint_to_checked.rs +++ /dev/null @@ -1,71 +0,0 @@ -use core::slice::from_raw_parts; - -use pinocchio::{ - account_info::AccountInfo, - instruction::{AccountMeta, Instruction, Signer}, - program::invoke_signed, - ProgramResult, -}; - -use crate::{write_bytes, UNINIT_BYTE}; - -/// Mints new tokens to an account. -/// -/// ### Accounts: -/// 0. `[WRITE]` The mint. -/// 1. `[WRITE]` The account to mint tokens to. -/// 2. `[SIGNER]` The mint's minting authority. -/// -pub struct MintToChecked<'a> { - /// Mint Account. - pub mint: &'a AccountInfo, - /// Token Account. - pub token: &'a AccountInfo, - /// Mint Authority - pub mint_authority: &'a AccountInfo, - /// Amount - pub amount: u64, - /// Decimals - pub decimals: u8, -} - -impl<'a> MintToChecked<'a> { - #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) - } - - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { - // account metadata - let account_metas: [AccountMeta; 3] = [ - AccountMeta::writable(self.mint.key()), - AccountMeta::writable(self.token.key()), - AccountMeta::readonly_signer(self.mint_authority.key()), - ]; - - // Instruction data layout: - // - [0]: instruction discriminator - // - [1..9]: amount - // - [9]: decimals - let mut instruction_data = [UNINIT_BYTE; 10]; - - // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[14]); - // Set amount as u64 at offset [1..9] - write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes()); - // Set decimals as u8 at offset [9] - write_bytes(&mut instruction_data[9..], &[self.decimals]); - - let instruction = Instruction { - program_id: &crate::ID, - accounts: &account_metas, - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 10) }, - }; - - invoke_signed( - &instruction, - &[self.mint, self.token, self.mint_authority], - signers, - ) - } -} diff --git a/programs/token2022/src/instructions/mod.rs b/programs/token2022/src/instructions/mod.rs deleted file mode 100644 index 9287f8f..0000000 --- a/programs/token2022/src/instructions/mod.rs +++ /dev/null @@ -1,39 +0,0 @@ -mod approve; -mod approve_checked; -mod burn; -mod burn_checked; -mod close_account; -mod freeze_account; -mod initialize_account; -mod initialize_account_2; -mod initialize_account_3; -mod initialize_mint; -mod initialize_mint_2; -mod mint_to; -mod mint_to_checked; -mod revoke; -mod set_authority; -mod sync_native; -mod thaw_account; -mod transfer; -mod transfer_checked; - -pub use approve::*; -pub use approve_checked::*; -pub use burn::*; -pub use burn_checked::*; -pub use close_account::*; -pub use freeze_account::*; -pub use initialize_account::*; -pub use initialize_account_2::*; -pub use initialize_account_3::*; -pub use initialize_mint::*; -pub use initialize_mint_2::*; -pub use mint_to::*; -pub use mint_to_checked::*; -pub use revoke::*; -pub use set_authority::*; -pub use sync_native::*; -pub use thaw_account::*; -pub use transfer::*; -pub use transfer_checked::*; diff --git a/programs/token2022/src/instructions/revoke.rs b/programs/token2022/src/instructions/revoke.rs deleted file mode 100644 index 75a7f6b..0000000 --- a/programs/token2022/src/instructions/revoke.rs +++ /dev/null @@ -1,41 +0,0 @@ -use pinocchio::{ - account_info::AccountInfo, - instruction::{AccountMeta, Instruction, Signer}, - program::invoke_signed, - ProgramResult, -}; - -/// Revokes the delegate's authority. -/// -/// ### Accounts: -/// 0. `[WRITE]` The source account. -/// 1. `[SIGNER]` The source account owner. -pub struct Revoke<'a> { - /// New Account. - pub token: &'a AccountInfo, - /// Mint Account. - pub authority: &'a AccountInfo, -} - -impl<'a> Revoke<'a> { - #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) - } - - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { - // account metadata - let account_metas: [AccountMeta; 2] = [ - AccountMeta::writable(self.token.key()), - AccountMeta::readonly_signer(self.authority.key()), - ]; - - let instruction = Instruction { - program_id: &crate::ID, - accounts: &account_metas, - data: &[5], - }; - - invoke_signed(&instruction, &[self.token, self.authority], signers) - } -} diff --git a/programs/token2022/src/instructions/set_authority.rs b/programs/token2022/src/instructions/set_authority.rs deleted file mode 100644 index a658349..0000000 --- a/programs/token2022/src/instructions/set_authority.rs +++ /dev/null @@ -1,80 +0,0 @@ -use core::slice::from_raw_parts; - -use pinocchio::{ - account_info::AccountInfo, - instruction::{AccountMeta, Instruction, Signer}, - program::invoke_signed, - pubkey::Pubkey, - ProgramResult, -}; - -use crate::{write_bytes, UNINIT_BYTE}; - -#[repr(u8)] -#[derive(Clone, Copy)] -pub enum AuthorityType { - MintTokens = 0, - FreezeAccount = 1, - AccountOwner = 2, - CloseAccount = 3, -} - -/// Sets a new authority of a mint or account. -/// -/// ### Accounts: -/// 0. `[WRITE]` The mint or account to change the authority of. -/// 1. `[SIGNER]` The current authority of the mint or account. -pub struct SetAuthority<'a> { - /// Account (Mint or Token) - pub account: &'a AccountInfo, - - /// Authority of the Account. - pub authority: &'a AccountInfo, - - /// The type of authority to update. - pub authority_type: AuthorityType, - - /// The new authority - pub new_authority: Option<&'a Pubkey>, -} - -impl<'a> SetAuthority<'a> { - #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) - } - - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { - // account metadata - let account_metas: [AccountMeta; 2] = [ - AccountMeta::writable(self.account.key()), - AccountMeta::readonly_signer(self.authority.key()), - ]; - - // instruction data - // - [0]: instruction discriminator - // - [1]: authority_type - // - [2..35] new_authority - let mut instruction_data = [UNINIT_BYTE; 35]; - - // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[6]); - // Set authority_type as u8 at offset [1] - write_bytes(&mut instruction_data[1..2], &[self.authority_type as u8]); - // Set new_authority as [u8; 32] at offset [2..35] - if let Some(new_authority) = self.new_authority { - write_bytes(&mut instruction_data[2..3], &[1]); - write_bytes(&mut instruction_data[3..], new_authority); - } else { - write_bytes(&mut instruction_data[2..3], &[0]); - } - - let instruction = Instruction { - program_id: &crate::ID, - accounts: &account_metas, - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 35) }, - }; - - invoke_signed(&instruction, &[self.account, self.authority], signers) - } -} diff --git a/programs/token2022/src/instructions/sync_native.rs b/programs/token2022/src/instructions/sync_native.rs deleted file mode 100644 index 46a192e..0000000 --- a/programs/token2022/src/instructions/sync_native.rs +++ /dev/null @@ -1,37 +0,0 @@ -use pinocchio::{ - account_info::AccountInfo, - instruction::{AccountMeta, Instruction, Signer}, - program::invoke_signed, - ProgramResult, -}; - -/// Given a native token account updates its amount field based -/// on the account's underlying `lamports`. -/// -/// ### Accounts: -/// 0. `[WRITE]` The native token account to sync with its underlying -/// lamports. -pub struct SyncNative<'a> { - /// Native Token Account - pub native_token: &'a AccountInfo, -} - -impl<'a> SyncNative<'a> { - #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) - } - - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { - // account metadata - let account_metas: [AccountMeta; 1] = [AccountMeta::writable(self.native_token.key())]; - - let instruction = Instruction { - program_id: &crate::ID, - accounts: &account_metas, - data: &[17], - }; - - invoke_signed(&instruction, &[self.native_token], signers) - } -} diff --git a/programs/token2022/src/instructions/thaw_account.rs b/programs/token2022/src/instructions/thaw_account.rs deleted file mode 100644 index 8353716..0000000 --- a/programs/token2022/src/instructions/thaw_account.rs +++ /dev/null @@ -1,49 +0,0 @@ -use pinocchio::{ - account_info::AccountInfo, - instruction::{AccountMeta, Instruction, Signer}, - program::invoke_signed, - ProgramResult, -}; - -/// Thaw a Frozen account using the Mint's freeze_authority -/// -/// ### Accounts: -/// 0. `[WRITE]` The account to thaw. -/// 1. `[]` The token mint. -/// 2. `[SIGNER]` The mint freeze authority. -pub struct ThawAccount<'a> { - /// Token Account to thaw. - pub token: &'a AccountInfo, - /// Mint Account. - pub mint: &'a AccountInfo, - /// Mint Freeze Authority Account - pub freeze_authority: &'a AccountInfo, -} - -impl<'a> ThawAccount<'a> { - #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) - } - - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { - // account metadata - let account_metas: [AccountMeta; 3] = [ - AccountMeta::writable(self.token.key()), - AccountMeta::readonly(self.mint.key()), - AccountMeta::readonly_signer(self.freeze_authority.key()), - ]; - - let instruction = Instruction { - program_id: &crate::ID, - accounts: &account_metas, - data: &[11], - }; - - invoke_signed( - &instruction, - &[self.token, self.mint, self.freeze_authority], - signers, - ) - } -} diff --git a/programs/token2022/src/instructions/transfer.rs b/programs/token2022/src/instructions/transfer.rs deleted file mode 100644 index 027b139..0000000 --- a/programs/token2022/src/instructions/transfer.rs +++ /dev/null @@ -1,61 +0,0 @@ -use core::slice::from_raw_parts; - -use pinocchio::{ - account_info::AccountInfo, - instruction::{AccountMeta, Instruction, Signer}, - program::invoke_signed, - ProgramResult, -}; - -use crate::{write_bytes, UNINIT_BYTE}; - -/// Transfer Tokens from one Token Account to another. -/// -/// ### Accounts: -/// 0. `[WRITE]` Sender account -/// 1. `[WRITE]` Recipient account -/// 2. `[SIGNER]` Authority account -pub struct Transfer<'a> { - /// Sender account. - pub from: &'a AccountInfo, - /// Recipient account. - pub to: &'a AccountInfo, - /// Authority account. - pub authority: &'a AccountInfo, - /// Amount of microtokens to transfer. - pub amount: u64, -} - -impl<'a> Transfer<'a> { - #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) - } - - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { - // account metadata - let account_metas: [AccountMeta; 3] = [ - AccountMeta::writable(self.from.key()), - AccountMeta::writable(self.to.key()), - AccountMeta::readonly_signer(self.authority.key()), - ]; - - // Instruction data layout: - // - [0]: instruction discriminator - // - [1..9]: amount - let mut instruction_data = [UNINIT_BYTE; 9]; - - // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[3]); - // Set amount as u64 at offset [1..9] - write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes()); - - let instruction = Instruction { - program_id: &crate::ID, - accounts: &account_metas, - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 9) }, - }; - - invoke_signed(&instruction, &[self.from, self.to, self.authority], signers) - } -} diff --git a/programs/token2022/src/instructions/transfer_checked.rs b/programs/token2022/src/instructions/transfer_checked.rs deleted file mode 100644 index 3ebe826..0000000 --- a/programs/token2022/src/instructions/transfer_checked.rs +++ /dev/null @@ -1,70 +0,0 @@ -use core::slice::from_raw_parts; - -use pinocchio::{ - account_info::AccountInfo, - instruction::{AccountMeta, Instruction, Signer}, - program::invoke_signed, - ProgramResult, -}; - -use crate::{write_bytes, UNINIT_BYTE}; - -/// Transfer Tokens from one Token Account to another. -/// -/// ### Accounts: -/// 0. `[WRITE]` The source account. -/// 1. `[]` The token mint. -/// 2. `[WRITE]` The destination account. -/// 3. `[SIGNER]` The source account's owner/delegate. -pub struct TransferChecked<'a> { - /// Sender account. - pub from: &'a AccountInfo, - /// Mint Account - pub mint: &'a AccountInfo, - /// Recipient account. - pub to: &'a AccountInfo, - /// Authority account. - pub authority: &'a AccountInfo, - /// Amount of microtokens to transfer. - pub amount: u64, - /// Decimal for the Token - pub decimals: u8, -} - -impl<'a> TransferChecked<'a> { - #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) - } - - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { - // account metadata - let account_metas: [AccountMeta; 4] = [ - AccountMeta::writable(self.from.key()), - AccountMeta::readonly(self.mint.key()), - AccountMeta::writable(self.to.key()), - AccountMeta::readonly_signer(self.authority.key()), - ]; - - // Instruction data layout: - // - [0]: instruction discriminator - // - [1..9]: amount - // - [9]: decimals - let mut instruction_data = [UNINIT_BYTE; 10]; - - // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[12]); - // Set amount as u64 at offset [1..9] - write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes()); - // Set decimals as u8 at offset [9] - write_bytes(&mut instruction_data[9..], &[self.decimals]); - - let instruction = Instruction { - program_id: &crate::ID, - accounts: &account_metas, - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 10) }, - }; - - invoke_signed(&instruction, &[self.from, self.to, self.authority], signers) - } -} diff --git a/programs/token2022/src/lib.rs b/programs/token2022/src/lib.rs index 01b8d20..7d37e4c 100644 --- a/programs/token2022/src/lib.rs +++ b/programs/token2022/src/lib.rs @@ -1,7 +1,6 @@ #![no_std] pub mod extensions; -pub mod instructions; pub mod state; pinocchio_pubkey::declare_id!("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"); From 842cfcf0a53c8e0791ce7f80dbd978a96f4c1338 Mon Sep 17 00:00:00 2001 From: NagaprasadVr Date: Fri, 13 Dec 2024 08:20:41 +0530 Subject: [PATCH 10/13] WIP : add cpi_guard and memo --- .../src/extensions/confidential_transfer.rs | 110 +++++++++++++ .../token2022/src/extensions/cpi_guard.rs | 148 +++++++++++++++++ .../src/extensions/default_account_state.rs | 82 ++++++++++ .../token2022/src/extensions/memo_transfer.rs | 150 ++++++++++++++++++ programs/token2022/src/extensions/mod.rs | 8 + .../token2022/src/extensions/transfer_fee.rs | 2 - 6 files changed, 498 insertions(+), 2 deletions(-) create mode 100644 programs/token2022/src/extensions/confidential_transfer.rs create mode 100644 programs/token2022/src/extensions/cpi_guard.rs create mode 100644 programs/token2022/src/extensions/default_account_state.rs create mode 100644 programs/token2022/src/extensions/memo_transfer.rs diff --git a/programs/token2022/src/extensions/confidential_transfer.rs b/programs/token2022/src/extensions/confidential_transfer.rs new file mode 100644 index 0000000..818f737 --- /dev/null +++ b/programs/token2022/src/extensions/confidential_transfer.rs @@ -0,0 +1,110 @@ +use core::slice::from_raw_parts; + +use pinocchio::{ + account_info::AccountInfo, + instruction::{AccountMeta, Instruction, Signer}, + program::invoke_signed, + pubkey::Pubkey, + ProgramResult, +}; + +use crate::{write_bytes, ID, UNINIT_BYTE}; + +use super::ElagamalPubkey; + +// Instructions + +/// Initialize a new mint for a confidential transfer. +pub struct InitializeMint<'a> { + pub mint: &'a AccountInfo, + /// Authority to modify the `ConfidentialTransferMint` configuration and to + /// approve new accounts. + pub authority: Option<&'a Pubkey>, + /// Determines if newly configured accounts must be approved by the + /// `authority` before they may be used by the user. + pub auto_approve_new_accounts: bool, + /// New authority to decode any transfer amount in a confidential transfer. + pub auditor_elgamal_pubkey: Option<&'a ElagamalPubkey>, +} + +impl<'a> InitializeMint<'a> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + // Account metadata + let account_metas: [AccountMeta; 1] = [AccountMeta::writable(self.mint.key())]; + + // Instruction data layout: + // - [0]: instruction discriminator (1 byte, u8) + let mut instruction_data = [UNINIT_BYTE; 1]; + + // Set discriminator as u8 at offset [0] + write_bytes(&mut instruction_data, &[0]); + + write_bytes( + &mut instruction_data[1..2], + &[self.auto_approve_new_accounts as u8], + ); + + if let Some(authority) = self.authority { + // Set authority as Pubkey at offset [2..34] + write_bytes(&mut instruction_data[2..3], &[1]); + write_bytes(&mut instruction_data[2..34], authority); + } else { + write_bytes(&mut instruction_data[2..3], &[0]); + } + + let instruction = Instruction { + program_id: &ID, + accounts: &account_metas, + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 1) }, + }; + + invoke_signed(&instruction, &[self.mint], signers) + } +} + +pub struct UpdateMint<'a> { + /// Mint Account. + pub mint: &'a AccountInfo, + /// `ConfidentialTransfer` transfer mint authority.. + pub mint_authority: &'a Pubkey, + /// Determines if newly configured accounts must be approved by the + /// `authority` before they may be used by the user. + pub auto_approve_new_accounts: bool, + /// New authority to decode any transfer amount in a confidential transfer. + pub auditor_elgamal_pubkey: Option<&'a ElagamalPubkey>, +} + +impl<'a> UpdateMint<'a> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + // Account metadata + let account_metas: [AccountMeta; 1] = [AccountMeta::writable(self.mint.key())]; + + // Instruction data layout: + // - [0]: instruction discriminator (1 byte, u8) + // - [1..33]: mint_authority (32 bytes, Pubkey) + let mut instruction_data = [UNINIT_BYTE; 33]; + + // Set discriminator as u8 at offset [0] + write_bytes(&mut instruction_data, &[1]); + // Set mint_authority as Pubkey at offset [1..33] + write_bytes(&mut instruction_data[1..33], self.mint_authority); + + let instruction = Instruction { + program_id: &ID, + accounts: &account_metas, + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 33) }, + }; + + invoke_signed(&instruction, &[self.mint], signers) + } +} diff --git a/programs/token2022/src/extensions/cpi_guard.rs b/programs/token2022/src/extensions/cpi_guard.rs new file mode 100644 index 0000000..8819ce4 --- /dev/null +++ b/programs/token2022/src/extensions/cpi_guard.rs @@ -0,0 +1,148 @@ +use pinocchio::{ + account_info::{AccountInfo, Ref}, + instruction::{AccountMeta, Instruction, Signer}, + program::invoke_signed, + program_error::ProgramError, +}; + +use crate::{write_bytes, ID, UNINIT_BYTE}; + +pub struct CpiGuard { + /// Lock privileged token operations from happening via CPI + pub lock_cpi: bool, +} + +impl CpiGuard { + /// The length of the `CpiGuard` account data. + pub const LEN: usize = core::mem::size_of::(); + + /// Return a `CpiGuard` from the given account info. + /// + /// This method performs owner and length validation on `AccountInfo`, safe borrowing + /// the account data. + #[inline(always)] + pub fn from_account_info(account_info: &AccountInfo) -> Result, ProgramError> { + if account_info.data_len() != Self::LEN { + return Err(ProgramError::InvalidAccountData); + } + if account_info.owner() != &ID { + return Err(ProgramError::InvalidAccountOwner); + } + Ok(Ref::map(account_info.try_borrow_data()?, |data| unsafe { + Self::from_bytes(data) + })) + } + + /// Return a `CpiGuard` from the given account info. + /// + /// This method performs owner and length validation on `AccountInfo`, but does not + /// perform the borrow check. + /// + /// # Safety + /// + /// The caller must ensure that it is safe to borrow the account data – e.g., there are + /// no mutable borrows of the account data. + #[inline] + pub unsafe fn from_account_info_unchecked( + account_info: &AccountInfo, + ) -> Result<&Self, ProgramError> { + if account_info.data_len() != Self::LEN { + return Err(ProgramError::InvalidAccountData); + } + if account_info.owner() != &ID { + return Err(ProgramError::InvalidAccountOwner); + } + Ok(Self::from_bytes(account_info.borrow_data_unchecked())) + } + + /// Return a `CpiGuard` from the given bytes. + /// + /// # Safety + /// + /// The caller must ensure that `bytes` contains a valid representation of `CpiGuard`. + #[inline(always)] + pub unsafe fn from_bytes(bytes: &[u8]) -> &Self { + &*(bytes.as_ptr() as *const &CpiGuard) + } +} + +// Instructions +pub struct EnableCpiGuard<'a> { + /// Account to enable the CPI guard + pub account: &'a AccountInfo, + /// The account's owner + pub account_owner: &'a AccountInfo, +} + +impl<'a> EnableCpiGuard<'a> { + #[inline(always)] + pub fn invoke(&self) -> Result<(), ProgramError> { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> Result<(), ProgramError> { + let account_metas = [ + AccountMeta::writable(self.account.key()), + AccountMeta::readonly_signer(self.account_owner.key()), + ]; + + // Instruction data Layout: + // - [0]: instruction discriminator (1 byte, u8) + let mut instruction_data = [UNINIT_BYTE; 2]; + + // Set discriminator as u8 at offset [0] + write_bytes(&mut instruction_data[0..1], todo!()); + + // Enable the CPI guard + write_bytes(&mut instruction_data[1..2], &[0]); + + let instruction = Instruction { + program_id: &ID, + accounts: &account_metas, + data: unsafe { core::slice::from_raw_parts(instruction_data.as_ptr() as _, 2) }, + }; + + Ok(()) + } +} + +pub struct DisableCpiGuard<'a> { + /// Account to disable the CPI guard + pub account: &'a AccountInfo, + /// The account's owner + pub account_owner: &'a AccountInfo, +} + +impl<'a> DisableCpiGuard<'a> { + #[inline(always)] + pub fn invoke(&self) -> Result<(), ProgramError> { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> Result<(), ProgramError> { + let account_metas = [ + AccountMeta::writable(self.account.key()), + AccountMeta::readonly_signer(self.account_owner.key()), + ]; + + // Instruction data Layout: + // - [0]: instruction discriminator (1 byte, u8) + let mut instruction_data = [UNINIT_BYTE; 2]; + + // Set discriminator as u8 at offset [0] + write_bytes(&mut instruction_data[0..1], todo!()); + + // Disable the CPI guard + write_bytes(&mut instruction_data[1..2], &[1]); + + let instruction = Instruction { + program_id: &ID, + accounts: &account_metas, + data: unsafe { core::slice::from_raw_parts(instruction_data.as_ptr() as _, 2) }, + }; + + invoke_signed(&instruction, &[self.account, self.account_owner], signers)?; + + Ok(()) + } +} diff --git a/programs/token2022/src/extensions/default_account_state.rs b/programs/token2022/src/extensions/default_account_state.rs new file mode 100644 index 0000000..834e197 --- /dev/null +++ b/programs/token2022/src/extensions/default_account_state.rs @@ -0,0 +1,82 @@ +use pinocchio::{ + account_info::{AccountInfo, Ref}, + program_error::ProgramError, +}; + +use crate::{state::AccountState, ID}; + +pub struct DefaultAccountState { + pub state: AccountState, +} + +impl DefaultAccountState { + /// The length of the `MemoTranfer` account data. + pub const LEN: usize = core::mem::size_of::(); + + /// Return a `TransferFeeConfig` from the given account info. + /// + /// This method performs owner and length validation on `AccountInfo`, safe borrowing + /// the account data. + #[inline(always)] + pub fn from_account_info( + account_info: &AccountInfo, + ) -> Result, ProgramError> { + if account_info.data_len() != Self::LEN { + return Err(ProgramError::InvalidAccountData); + } + if account_info.owner() != &ID { + return Err(ProgramError::InvalidAccountOwner); + } + Ok(Ref::map(account_info.try_borrow_data()?, |data| unsafe { + Self::from_bytes(data) + })) + } + + /// Return a `MemoTransfer` from the given account info. + /// + /// This method performs owner and length validation on `AccountInfo`, but does not + /// perform the borrow check. + /// + /// # Safety + /// + /// The caller must ensure that it is safe to borrow the account data – e.g., there are + /// no mutable borrows of the account data. + #[inline] + pub unsafe fn from_account_info_unchecked( + account_info: &AccountInfo, + ) -> Result<&Self, ProgramError> { + if account_info.data_len() != Self::LEN { + return Err(ProgramError::InvalidAccountData); + } + if account_info.owner() != &ID { + return Err(ProgramError::InvalidAccountOwner); + } + Ok(Self::from_bytes(account_info.borrow_data_unchecked())) + } + + /// Return a `TransferFeeConfig` from the given bytes. + /// + /// # Safety + /// + /// The caller must ensure that `bytes` contains a valid representation of `TransferFeeConfig`. + #[inline(always)] + pub unsafe fn from_bytes(bytes: &[u8]) -> &Self { + &*(bytes.as_ptr() as *const &DefaultAccountState) + } +} + +pub struct InitializeDefaultAccountState<'a> { + /// The mint to initialize + pub mint: &'a AccountInfo, + /// Default account state + pub state: u8, +} + +pub struct UpdateDefaultAccountState<'a> { + /// The mint to update + pub mint: &'a AccountInfo, + /// The mint's freeze authority + pub mint_freeze_authority: &'a AccountInfo, + /// The new state + pub new_state: u8, +} diff --git a/programs/token2022/src/extensions/memo_transfer.rs b/programs/token2022/src/extensions/memo_transfer.rs new file mode 100644 index 0000000..412748c --- /dev/null +++ b/programs/token2022/src/extensions/memo_transfer.rs @@ -0,0 +1,150 @@ +use pinocchio::{ + account_info::{AccountInfo, Ref}, + instruction::{AccountMeta, Instruction, Signer}, + program::invoke_signed, + program_error::ProgramError, +}; + +use crate::{write_bytes, ID, UNINIT_BYTE}; +// State +pub struct MemoTransfer { + /// Require transfers into this account to be accompanied by a memo + pub require_incoming_transfer_memos: bool, +} + +impl MemoTransfer { + /// The length of the `MemoTranfer` account data. + pub const LEN: usize = core::mem::size_of::(); + + /// Return a `TransferFeeConfig` from the given account info. + /// + /// This method performs owner and length validation on `AccountInfo`, safe borrowing + /// the account data. + #[inline(always)] + pub fn from_account_info( + account_info: &AccountInfo, + ) -> Result, ProgramError> { + if account_info.data_len() != Self::LEN { + return Err(ProgramError::InvalidAccountData); + } + if account_info.owner() != &ID { + return Err(ProgramError::InvalidAccountOwner); + } + Ok(Ref::map(account_info.try_borrow_data()?, |data| unsafe { + Self::from_bytes(data) + })) + } + + /// Return a `MemoTransfer` from the given account info. + /// + /// This method performs owner and length validation on `AccountInfo`, but does not + /// perform the borrow check. + /// + /// # Safety + /// + /// The caller must ensure that it is safe to borrow the account data – e.g., there are + /// no mutable borrows of the account data. + #[inline] + pub unsafe fn from_account_info_unchecked( + account_info: &AccountInfo, + ) -> Result<&Self, ProgramError> { + if account_info.data_len() != Self::LEN { + return Err(ProgramError::InvalidAccountData); + } + if account_info.owner() != &ID { + return Err(ProgramError::InvalidAccountOwner); + } + Ok(Self::from_bytes(account_info.borrow_data_unchecked())) + } + + /// Return a `TransferFeeConfig` from the given bytes. + /// + /// # Safety + /// + /// The caller must ensure that `bytes` contains a valid representation of `TransferFeeConfig`. + #[inline(always)] + pub unsafe fn from_bytes(bytes: &[u8]) -> &Self { + &*(bytes.as_ptr() as *const &MemoTransfer) + } +} + +// Instructions + +pub struct EnableMemoTransfer<'a> { + /// The account to update. + pub account: &'a AccountInfo, + /// The account owner. + pub account_owner: &'a AccountInfo, +} + +impl<'a> EnableMemoTransfer<'a> { + #[inline(always)] + pub fn invoke(&self) -> Result<(), ProgramError> { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> Result<(), ProgramError> { + // account metadata + let account_metas = [ + AccountMeta::writable(self.account.key()), + AccountMeta::readonly_signer(self.account_owner.key()), + ]; + + // Instruction data Layout + // - [0]: instruction discriminator (1 byte, u8) + let mut instruction_data = [UNINIT_BYTE; 2]; + + // Set discriminator as u8 at offset [0] + write_bytes(&mut instruction_data[0..1], &[todo!()]); + + // Enable incoming transfer memos + write_bytes(&mut instruction_data[1..2], &[0]); + + let instruction = Instruction { + program_id: &crate::ID, + accounts: &account_metas, + data: unsafe { core::slice::from_raw_parts(instruction_data.as_ptr() as _, 2) }, + }; + + invoke_signed(&instruction, &[self.account], signers) + } +} + +pub struct DisableMemoTransfer<'a> { + /// The account to update. + pub account: &'a AccountInfo, + /// The account owner. + pub account_owner: &'a AccountInfo, +} + +impl<'a> DisableMemoTransfer<'a> { + #[inline(always)] + pub fn invoke(&self) -> Result<(), ProgramError> { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> Result<(), ProgramError> { + // account metadata + let account_metas = [ + AccountMeta::writable(self.account.key()), + AccountMeta::readonly_signer(self.account_owner.key()), + ]; + + // instruction data + // - [0]: instruction discriminator (1 byte, u8) + let mut instruction_data = [UNINIT_BYTE; 2]; + + // Set discriminator as u8 at offset [0] + write_bytes(&mut instruction_data[0..1], &[todo!()]); + // Disable incoming transfer memos + write_bytes(&mut instruction_data[1..2], &[1]); + + let instruction = Instruction { + program_id: &crate::ID, + accounts: &account_metas, + data: unsafe { core::slice::from_raw_parts(instruction_data.as_ptr() as _, 1) }, + }; + + invoke_signed(&instruction, &[self.account, self.account_owner], signers) + } +} diff --git a/programs/token2022/src/extensions/mod.rs b/programs/token2022/src/extensions/mod.rs index c245c84..e2fcb1f 100644 --- a/programs/token2022/src/extensions/mod.rs +++ b/programs/token2022/src/extensions/mod.rs @@ -1 +1,9 @@ +pub mod confidential_transfer; +pub mod cpi_guard; +pub mod default_account_state; +pub mod memo_transfer; pub mod transfer_fee; + +pub const ELGAMAL_PUBKEY_LEN: usize = 32; + +pub struct ElagamalPubkey(pub [u8; ELGAMAL_PUBKEY_LEN]); diff --git a/programs/token2022/src/extensions/transfer_fee.rs b/programs/token2022/src/extensions/transfer_fee.rs index 13ea879..f9ee4c4 100644 --- a/programs/token2022/src/extensions/transfer_fee.rs +++ b/programs/token2022/src/extensions/transfer_fee.rs @@ -11,8 +11,6 @@ use pinocchio::{ use crate::{write_bytes, ID, UNINIT_BYTE}; -pub const MAX_ACCOUNTS_FOR_WITHDRAW: usize = 10; - /// Transfer fee configuration #[repr(C)] pub struct TransferFee { From 54e4d376df09bf96ef17c62cd30d6c9113fd4993 Mon Sep 17 00:00:00 2001 From: NagaprasadVr Date: Fri, 13 Dec 2024 09:23:13 +0530 Subject: [PATCH 11/13] merge legacy and token-2022 to single client and use program_ids to handle ixs and acc fetch logic separately --- Cargo.lock | 8 - Cargo.toml | 1 - programs/token/Cargo.toml | 4 +- programs/token/README.md | 6 +- .../src/extensions/confidential_transfer.rs | 6 +- .../src/extensions/cpi_guard.rs | 10 +- .../src/extensions/default_account_state.rs | 6 +- .../src/extensions/memo_transfer.rs | 10 +- .../src/extensions/mod.rs | 0 .../src/extensions/transfer_fee.rs | 18 +- programs/token/src/instructions/approve.rs | 14 +- .../token/src/instructions/approve_checked.rs | 13 +- programs/token/src/instructions/burn.rs | 14 +- .../token/src/instructions/burn_checked.rs | 14 +- .../token/src/instructions/close_account.rs | 14 +- .../token/src/instructions/freeze_account.rs | 14 +- .../src/instructions/initialize_account.rs | 14 +- .../src/instructions/initialize_account_2.rs | 14 +- .../src/instructions/initialize_account_3.rs | 14 +- .../token/src/instructions/initialize_mint.rs | 14 +- .../src/instructions/initialize_mint_2.rs | 14 +- programs/token/src/instructions/mint_to.rs | 14 +- .../token/src/instructions/mint_to_checked.rs | 14 +- programs/token/src/instructions/mod.rs | 20 ++ programs/token/src/instructions/revoke.rs | 14 +- .../token/src/instructions/set_authority.rs | 14 +- .../token/src/instructions/sync_native.rs | 14 +- .../token/src/instructions/thaw_account.rs | 14 +- programs/token/src/instructions/transfer.rs | 14 +- .../src/instructions/transfer_checked.rs | 14 +- programs/token/src/lib.rs | 7 +- programs/token/src/state/mint.rs | 21 +- programs/token/src/state/token.rs | 16 +- programs/token2022/Cargo.toml | 12 -- programs/token2022/README.md | 42 ---- programs/token2022/src/lib.rs | 27 --- programs/token2022/src/state/account_state.rs | 36 ---- programs/token2022/src/state/mint.rs | 145 ------------- programs/token2022/src/state/mod.rs | 8 - programs/token2022/src/state/token.rs | 198 ------------------ 40 files changed, 272 insertions(+), 594 deletions(-) rename programs/{token2022 => token}/src/extensions/confidential_transfer.rs (95%) rename programs/{token2022 => token}/src/extensions/cpi_guard.rs (94%) rename programs/{token2022 => token}/src/extensions/default_account_state.rs (93%) rename programs/{token2022 => token}/src/extensions/memo_transfer.rs (94%) rename programs/{token2022 => token}/src/extensions/mod.rs (100%) rename programs/{token2022 => token}/src/extensions/transfer_fee.rs (97%) delete mode 100644 programs/token2022/Cargo.toml delete mode 100644 programs/token2022/README.md delete mode 100644 programs/token2022/src/lib.rs delete mode 100644 programs/token2022/src/state/account_state.rs delete mode 100644 programs/token2022/src/state/mint.rs delete mode 100644 programs/token2022/src/state/mod.rs delete mode 100644 programs/token2022/src/state/token.rs diff --git a/Cargo.lock b/Cargo.lock index a4515ac..00b8fdf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,14 +52,6 @@ dependencies = [ "pinocchio-pubkey", ] -[[package]] -name = "pinocchio-token" -version = "0.2.0" -dependencies = [ - "pinocchio", - "pinocchio-pubkey", -] - [[package]] name = "pinocchio-token2022" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index bcd4761..9709685 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,6 @@ resolver = "2" members = [ "programs/system", "programs/token", - "programs/token2022", "sdk/log/crate", "sdk/log/macro", "sdk/pinocchio", diff --git a/programs/token/Cargo.toml b/programs/token/Cargo.toml index fd5be9d..28b6666 100644 --- a/programs/token/Cargo.toml +++ b/programs/token/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "pinocchio-token" -description = "Pinocchio helpers to invoke Token program instructions" +name = "pinocchio-token2022" +description = "Pinocchio helpers to invoke Token program 2022 instructions" version = "0.2.0" edition = { workspace = true } license = { workspace = true } diff --git a/programs/token/README.md b/programs/token/README.md index db19b7a..9bfaa09 100644 --- a/programs/token/README.md +++ b/programs/token/README.md @@ -1,6 +1,6 @@ -# pinocchio-token +# pinocchio-token2022 -This crate contains [`pinocchio`](https://crates.io/crates/pinocchio) helpers to perform cross-program invocations (CPIs) for SPL Token instructions. +This crate contains [`pinocchio`](https://crates.io/crates/pinocchio) helpers to perform cross-program invocations (CPIs) for Token 2022 instructions. Each instruction defines an `struct` with the accounts and parameters required. Once all values are set, you can call directly `invoke` or `invoke_signed` to perform the CPI. @@ -11,6 +11,7 @@ This is a `no_std` crate. ## Examples Initializing a mint account: + ```rust // This example assumes that the instruction receives a writable `mint` // account; `authority` is a `Pubkey`. @@ -24,6 +25,7 @@ InitilizeMint { ``` Performing a transfer of tokens: + ```rust // This example assumes that the instruction receives writable `from` and `to` // accounts, and a signer `authority` account. diff --git a/programs/token2022/src/extensions/confidential_transfer.rs b/programs/token/src/extensions/confidential_transfer.rs similarity index 95% rename from programs/token2022/src/extensions/confidential_transfer.rs rename to programs/token/src/extensions/confidential_transfer.rs index 818f737..ca0f402 100644 --- a/programs/token2022/src/extensions/confidential_transfer.rs +++ b/programs/token/src/extensions/confidential_transfer.rs @@ -8,7 +8,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{write_bytes, ID, UNINIT_BYTE}; +use crate::{write_bytes, TOKEN_2022_PROGRAM_ID, UNINIT_BYTE}; use super::ElagamalPubkey; @@ -58,7 +58,7 @@ impl<'a> InitializeMint<'a> { } let instruction = Instruction { - program_id: &ID, + program_id: &TOKEN_2022_PROGRAM_ID, accounts: &account_metas, data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 1) }, }; @@ -100,7 +100,7 @@ impl<'a> UpdateMint<'a> { write_bytes(&mut instruction_data[1..33], self.mint_authority); let instruction = Instruction { - program_id: &ID, + program_id: &TOKEN_2022_PROGRAM_ID, accounts: &account_metas, data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 33) }, }; diff --git a/programs/token2022/src/extensions/cpi_guard.rs b/programs/token/src/extensions/cpi_guard.rs similarity index 94% rename from programs/token2022/src/extensions/cpi_guard.rs rename to programs/token/src/extensions/cpi_guard.rs index 8819ce4..89ceb2b 100644 --- a/programs/token2022/src/extensions/cpi_guard.rs +++ b/programs/token/src/extensions/cpi_guard.rs @@ -5,7 +5,7 @@ use pinocchio::{ program_error::ProgramError, }; -use crate::{write_bytes, ID, UNINIT_BYTE}; +use crate::{write_bytes, TOKEN_2022_PROGRAM_ID, UNINIT_BYTE}; pub struct CpiGuard { /// Lock privileged token operations from happening via CPI @@ -25,7 +25,7 @@ impl CpiGuard { if account_info.data_len() != Self::LEN { return Err(ProgramError::InvalidAccountData); } - if account_info.owner() != &ID { + if account_info.owner() != &TOKEN_2022_PROGRAM_ID { return Err(ProgramError::InvalidAccountOwner); } Ok(Ref::map(account_info.try_borrow_data()?, |data| unsafe { @@ -49,7 +49,7 @@ impl CpiGuard { if account_info.data_len() != Self::LEN { return Err(ProgramError::InvalidAccountData); } - if account_info.owner() != &ID { + if account_info.owner() != &TOKEN_2022_PROGRAM_ID { return Err(ProgramError::InvalidAccountOwner); } Ok(Self::from_bytes(account_info.borrow_data_unchecked())) @@ -97,7 +97,7 @@ impl<'a> EnableCpiGuard<'a> { write_bytes(&mut instruction_data[1..2], &[0]); let instruction = Instruction { - program_id: &ID, + program_id: &TOKEN_2022_PROGRAM_ID, accounts: &account_metas, data: unsafe { core::slice::from_raw_parts(instruction_data.as_ptr() as _, 2) }, }; @@ -136,7 +136,7 @@ impl<'a> DisableCpiGuard<'a> { write_bytes(&mut instruction_data[1..2], &[1]); let instruction = Instruction { - program_id: &ID, + program_id: &TOKEN_2022_PROGRAM_ID, accounts: &account_metas, data: unsafe { core::slice::from_raw_parts(instruction_data.as_ptr() as _, 2) }, }; diff --git a/programs/token2022/src/extensions/default_account_state.rs b/programs/token/src/extensions/default_account_state.rs similarity index 93% rename from programs/token2022/src/extensions/default_account_state.rs rename to programs/token/src/extensions/default_account_state.rs index 834e197..7b6fe5c 100644 --- a/programs/token2022/src/extensions/default_account_state.rs +++ b/programs/token/src/extensions/default_account_state.rs @@ -3,7 +3,7 @@ use pinocchio::{ program_error::ProgramError, }; -use crate::{state::AccountState, ID}; +use crate::{state::AccountState, TOKEN_2022_PROGRAM_ID}; pub struct DefaultAccountState { pub state: AccountState, @@ -24,7 +24,7 @@ impl DefaultAccountState { if account_info.data_len() != Self::LEN { return Err(ProgramError::InvalidAccountData); } - if account_info.owner() != &ID { + if account_info.owner() != &TOKEN_2022_PROGRAM_ID { return Err(ProgramError::InvalidAccountOwner); } Ok(Ref::map(account_info.try_borrow_data()?, |data| unsafe { @@ -48,7 +48,7 @@ impl DefaultAccountState { if account_info.data_len() != Self::LEN { return Err(ProgramError::InvalidAccountData); } - if account_info.owner() != &ID { + if account_info.owner() != &TOKEN_2022_PROGRAM_ID { return Err(ProgramError::InvalidAccountOwner); } Ok(Self::from_bytes(account_info.borrow_data_unchecked())) diff --git a/programs/token2022/src/extensions/memo_transfer.rs b/programs/token/src/extensions/memo_transfer.rs similarity index 94% rename from programs/token2022/src/extensions/memo_transfer.rs rename to programs/token/src/extensions/memo_transfer.rs index 412748c..b32d4ce 100644 --- a/programs/token2022/src/extensions/memo_transfer.rs +++ b/programs/token/src/extensions/memo_transfer.rs @@ -5,7 +5,7 @@ use pinocchio::{ program_error::ProgramError, }; -use crate::{write_bytes, ID, UNINIT_BYTE}; +use crate::{write_bytes, TOKEN_2022_PROGRAM_ID, UNINIT_BYTE}; // State pub struct MemoTransfer { /// Require transfers into this account to be accompanied by a memo @@ -27,7 +27,7 @@ impl MemoTransfer { if account_info.data_len() != Self::LEN { return Err(ProgramError::InvalidAccountData); } - if account_info.owner() != &ID { + if account_info.owner() != &TOKEN_2022_PROGRAM_ID { return Err(ProgramError::InvalidAccountOwner); } Ok(Ref::map(account_info.try_borrow_data()?, |data| unsafe { @@ -51,7 +51,7 @@ impl MemoTransfer { if account_info.data_len() != Self::LEN { return Err(ProgramError::InvalidAccountData); } - if account_info.owner() != &ID { + if account_info.owner() != &TOKEN_2022_PROGRAM_ID { return Err(ProgramError::InvalidAccountOwner); } Ok(Self::from_bytes(account_info.borrow_data_unchecked())) @@ -101,7 +101,7 @@ impl<'a> EnableMemoTransfer<'a> { write_bytes(&mut instruction_data[1..2], &[0]); let instruction = Instruction { - program_id: &crate::ID, + program_id: &crate::TOKEN_2022_PROGRAM_ID, accounts: &account_metas, data: unsafe { core::slice::from_raw_parts(instruction_data.as_ptr() as _, 2) }, }; @@ -140,7 +140,7 @@ impl<'a> DisableMemoTransfer<'a> { write_bytes(&mut instruction_data[1..2], &[1]); let instruction = Instruction { - program_id: &crate::ID, + program_id: &crate::TOKEN_2022_PROGRAM_ID, accounts: &account_metas, data: unsafe { core::slice::from_raw_parts(instruction_data.as_ptr() as _, 1) }, }; diff --git a/programs/token2022/src/extensions/mod.rs b/programs/token/src/extensions/mod.rs similarity index 100% rename from programs/token2022/src/extensions/mod.rs rename to programs/token/src/extensions/mod.rs diff --git a/programs/token2022/src/extensions/transfer_fee.rs b/programs/token/src/extensions/transfer_fee.rs similarity index 97% rename from programs/token2022/src/extensions/transfer_fee.rs rename to programs/token/src/extensions/transfer_fee.rs index f9ee4c4..ef46386 100644 --- a/programs/token2022/src/extensions/transfer_fee.rs +++ b/programs/token/src/extensions/transfer_fee.rs @@ -9,7 +9,7 @@ use pinocchio::{ ProgramResult, }; -use crate::{write_bytes, ID, UNINIT_BYTE}; +use crate::{write_bytes, TOKEN_2022_PROGRAM_ID, UNINIT_BYTE}; /// Transfer fee configuration #[repr(C)] @@ -59,7 +59,7 @@ impl TransferFeeConfig { if account_info.data_len() != Self::LEN { return Err(ProgramError::InvalidAccountData); } - if account_info.owner() != &ID { + if account_info.owner() != &TOKEN_2022_PROGRAM_ID { return Err(ProgramError::InvalidAccountOwner); } Ok(Ref::map(account_info.try_borrow_data()?, |data| unsafe { @@ -83,7 +83,7 @@ impl TransferFeeConfig { if account_info.data_len() != Self::LEN { return Err(ProgramError::InvalidAccountData); } - if account_info.owner() != &ID { + if account_info.owner() != &TOKEN_2022_PROGRAM_ID { return Err(ProgramError::InvalidAccountOwner); } Ok(Self::from_bytes(account_info.borrow_data_unchecked())) @@ -165,7 +165,7 @@ impl<'a> InitializeTransferFeeConfig<'a> { } let instruction = Instruction { - program_id: &crate::ID, + program_id: &crate::TOKEN_2022_PROGRAM_ID, accounts: &[AccountMeta::writable(self.mint.key())], data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 109) }, }; @@ -227,7 +227,7 @@ impl<'a> TransferCheckedWithFee<'a> { write_bytes(&mut instruction_data[10..18], &self.fee.to_le_bytes()); let instruction = Instruction { - program_id: &crate::ID, + program_id: &crate::TOKEN_2022_PROGRAM_ID, accounts: &account_metas, data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 18) }, }; @@ -270,7 +270,7 @@ impl<'a> WithdrawWithheldTokensFromMint<'a> { let instruction_data = [29]; let instruction = Instruction { - program_id: &crate::ID, + program_id: &crate::TOKEN_2022_PROGRAM_ID, accounts: &account_metas, data: &instruction_data, }; @@ -337,7 +337,7 @@ impl<'a, const ACCOUNTS_LEN: usize> WithdrawWithheldTokensFromAccounts<'a, ACCOU }; let instruction = Instruction { - program_id: &crate::ID, + program_id: &crate::TOKEN_2022_PROGRAM_ID, accounts: acc_metas, data: &instruction_data, }; @@ -405,7 +405,7 @@ impl<'a, const ACCOUNTS_LEN: usize> HarvestWithheldTokensToMint<'a, ACCOUNTS_LEN }; let instruction = Instruction { - program_id: &crate::ID, + program_id: &crate::TOKEN_2022_PROGRAM_ID, accounts: acc_metas, data: &instruction_data, }; @@ -476,7 +476,7 @@ impl<'a> SetTransferFee<'a> { ); let instruction = Instruction { - program_id: &crate::ID, + program_id: &crate::TOKEN_2022_PROGRAM_ID, accounts: &account_metas, data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 11) }, }; diff --git a/programs/token/src/instructions/approve.rs b/programs/token/src/instructions/approve.rs index d9596f7..19e5f79 100644 --- a/programs/token/src/instructions/approve.rs +++ b/programs/token/src/instructions/approve.rs @@ -9,6 +9,8 @@ use pinocchio::{ use crate::{write_bytes, UNINIT_BYTE}; +use super::TokenProgramVariant; + /// Approves a delegate. /// /// ### Accounts: @@ -28,11 +30,15 @@ pub struct Approve<'a> { impl<'a> Approve<'a> { #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) + pub fn invoke(&self, token_program: TokenProgramVariant) -> ProgramResult { + self.invoke_signed(&[], token_program) } - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + pub fn invoke_signed( + &self, + signers: &[Signer], + token_program: TokenProgramVariant, + ) -> ProgramResult { // Account metadata let account_metas: [AccountMeta; 3] = [ AccountMeta::writable(self.token.key()), @@ -51,7 +57,7 @@ impl<'a> Approve<'a> { write_bytes(&mut instruction_data[1..], &self.amount.to_le_bytes()); let instruction = Instruction { - program_id: &crate::ID, + program_id: &token_program.into(), accounts: &account_metas, data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 9) }, }; diff --git a/programs/token/src/instructions/approve_checked.rs b/programs/token/src/instructions/approve_checked.rs index 6195122..49fc2bd 100644 --- a/programs/token/src/instructions/approve_checked.rs +++ b/programs/token/src/instructions/approve_checked.rs @@ -7,6 +7,7 @@ use pinocchio::{ ProgramResult, }; +use super::TokenProgramVariant; use crate::{write_bytes, UNINIT_BYTE}; /// Approves a delegate. @@ -33,11 +34,15 @@ pub struct ApproveChecked<'a> { impl<'a> ApproveChecked<'a> { #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) + pub fn invoke(&self, token_program: TokenProgramVariant) -> ProgramResult { + self.invoke_signed(&[], token_program) } - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + pub fn invoke_signed( + &self, + signers: &[Signer], + token_program: TokenProgramVariant, + ) -> ProgramResult { // Account metadata let account_metas: [AccountMeta; 4] = [ AccountMeta::writable(self.token.key()), @@ -60,7 +65,7 @@ impl<'a> ApproveChecked<'a> { write_bytes(&mut instruction_data[9..], &[self.decimals]); let instruction = Instruction { - program_id: &crate::ID, + program_id: &token_program.into(), accounts: &account_metas, data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 10) }, }; diff --git a/programs/token/src/instructions/burn.rs b/programs/token/src/instructions/burn.rs index 4a8e71a..74619fa 100644 --- a/programs/token/src/instructions/burn.rs +++ b/programs/token/src/instructions/burn.rs @@ -9,6 +9,8 @@ use pinocchio::{ use crate::{write_bytes, UNINIT_BYTE}; +use super::TokenProgramVariant; + /// Burns tokens by removing them from an account. /// /// ### Accounts: @@ -28,11 +30,15 @@ pub struct Burn<'a> { impl<'a> Burn<'a> { #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) + pub fn invoke(&self, token_program: TokenProgramVariant) -> ProgramResult { + self.invoke_signed(&[], token_program) } - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + pub fn invoke_signed( + &self, + signers: &[Signer], + token_program: TokenProgramVariant, + ) -> ProgramResult { // Account metadata let account_metas: [AccountMeta; 3] = [ AccountMeta::writable(self.token.key()), @@ -51,7 +57,7 @@ impl<'a> Burn<'a> { write_bytes(&mut instruction_data[1..], &self.amount.to_le_bytes()); let instruction = Instruction { - program_id: &crate::ID, + program_id: &token_program.into(), accounts: &account_metas, data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 9) }, }; diff --git a/programs/token/src/instructions/burn_checked.rs b/programs/token/src/instructions/burn_checked.rs index 789a320..e221dd1 100644 --- a/programs/token/src/instructions/burn_checked.rs +++ b/programs/token/src/instructions/burn_checked.rs @@ -8,6 +8,8 @@ use pinocchio::{ ProgramResult, }; +use super::TokenProgramVariant; + /// Burns tokens by removing them from an account. /// /// ### Accounts: @@ -29,11 +31,15 @@ pub struct BurnChecked<'a> { impl<'a> BurnChecked<'a> { #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) + pub fn invoke(&self, token_program: TokenProgramVariant) -> ProgramResult { + self.invoke_signed(&[], token_program) } - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + pub fn invoke_signed( + &self, + signers: &[Signer], + token_program: TokenProgramVariant, + ) -> ProgramResult { // Account metadata let account_metas: [AccountMeta; 3] = [ AccountMeta::writable(self.token.key()), @@ -55,7 +61,7 @@ impl<'a> BurnChecked<'a> { write_bytes(&mut instruction_data[9..], &[self.decimals]); let instruction = Instruction { - program_id: &crate::ID, + program_id: &token_program.into(), accounts: &account_metas, data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 10) }, }; diff --git a/programs/token/src/instructions/close_account.rs b/programs/token/src/instructions/close_account.rs index 19461d9..9a367b0 100644 --- a/programs/token/src/instructions/close_account.rs +++ b/programs/token/src/instructions/close_account.rs @@ -5,6 +5,8 @@ use pinocchio::{ ProgramResult, }; +use super::TokenProgramVariant; + /// Close an account by transferring all its SOL to the destination account. /// /// ### Accounts: @@ -22,11 +24,15 @@ pub struct CloseAccount<'a> { impl<'a> CloseAccount<'a> { #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) + pub fn invoke(&self, token_program: TokenProgramVariant) -> ProgramResult { + self.invoke_signed(&[], token_program) } - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + pub fn invoke_signed( + &self, + signers: &[Signer], + token_program: TokenProgramVariant, + ) -> ProgramResult { // account metadata let account_metas: [AccountMeta; 3] = [ AccountMeta::writable(self.account.key()), @@ -35,7 +41,7 @@ impl<'a> CloseAccount<'a> { ]; let instruction = Instruction { - program_id: &crate::ID, + program_id: &token_program.into(), accounts: &account_metas, data: &[9], }; diff --git a/programs/token/src/instructions/freeze_account.rs b/programs/token/src/instructions/freeze_account.rs index a2c9c02..7c54baa 100644 --- a/programs/token/src/instructions/freeze_account.rs +++ b/programs/token/src/instructions/freeze_account.rs @@ -5,6 +5,8 @@ use pinocchio::{ ProgramResult, }; +use super::TokenProgramVariant; + /// Freeze an Initialized account using the Mint's freeze_authority /// /// ### Accounts: @@ -22,11 +24,15 @@ pub struct FreezeAccount<'a> { impl<'a> FreezeAccount<'a> { #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) + pub fn invoke(&self, token_program: TokenProgramVariant) -> ProgramResult { + self.invoke_signed(&[], token_program) } - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + pub fn invoke_signed( + &self, + signers: &[Signer], + token_program: TokenProgramVariant, + ) -> ProgramResult { // account metadata let account_metas: [AccountMeta; 3] = [ AccountMeta::writable(self.token.key()), @@ -35,7 +41,7 @@ impl<'a> FreezeAccount<'a> { ]; let instruction = Instruction { - program_id: &crate::ID, + program_id: &token_program.into(), accounts: &account_metas, data: &[10], }; diff --git a/programs/token/src/instructions/initialize_account.rs b/programs/token/src/instructions/initialize_account.rs index 5d5603d..4a682e5 100644 --- a/programs/token/src/instructions/initialize_account.rs +++ b/programs/token/src/instructions/initialize_account.rs @@ -5,6 +5,8 @@ use pinocchio::{ ProgramResult, }; +use super::TokenProgramVariant; + /// Initialize a new Token Account. /// /// ### Accounts: @@ -25,11 +27,15 @@ pub struct InitilizeAccount<'a> { impl<'a> InitilizeAccount<'a> { #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) + pub fn invoke(&self, token_program: TokenProgramVariant) -> ProgramResult { + self.invoke_signed(&[], token_program) } - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + pub fn invoke_signed( + &self, + signers: &[Signer], + token_program: TokenProgramVariant, + ) -> ProgramResult { // account metadata let account_metas: [AccountMeta; 4] = [ AccountMeta::writable(self.token.key()), @@ -39,7 +45,7 @@ impl<'a> InitilizeAccount<'a> { ]; let instruction = Instruction { - program_id: &crate::ID, + program_id: &token_program.into(), accounts: &account_metas, data: &[1], }; diff --git a/programs/token/src/instructions/initialize_account_2.rs b/programs/token/src/instructions/initialize_account_2.rs index 9ca72c7..ba3f52b 100644 --- a/programs/token/src/instructions/initialize_account_2.rs +++ b/programs/token/src/instructions/initialize_account_2.rs @@ -10,6 +10,8 @@ use pinocchio::{ use crate::{write_bytes, UNINIT_BYTE}; +use super::TokenProgramVariant; + /// Initialize a new Token Account. /// /// ### Accounts: @@ -29,11 +31,15 @@ pub struct InitilizeAccount2<'a> { impl<'a> InitilizeAccount2<'a> { #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) + pub fn invoke(&self, token_program: TokenProgramVariant) -> ProgramResult { + self.invoke_signed(&[], token_program) } - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + pub fn invoke_signed( + &self, + signers: &[Signer], + token_program: TokenProgramVariant, + ) -> ProgramResult { // account metadata let account_metas: [AccountMeta; 3] = [ AccountMeta::writable(self.token.key()), @@ -52,7 +58,7 @@ impl<'a> InitilizeAccount2<'a> { write_bytes(&mut instruction_data[1..], self.owner); let instruction = Instruction { - program_id: &crate::ID, + program_id: &token_program.into(), accounts: &account_metas, data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 33) }, }; diff --git a/programs/token/src/instructions/initialize_account_3.rs b/programs/token/src/instructions/initialize_account_3.rs index f3dc433..179cc8c 100644 --- a/programs/token/src/instructions/initialize_account_3.rs +++ b/programs/token/src/instructions/initialize_account_3.rs @@ -10,6 +10,8 @@ use pinocchio::{ use crate::{write_bytes, UNINIT_BYTE}; +use super::TokenProgramVariant; + /// Initialize a new Token Account. /// /// ### Accounts: @@ -26,11 +28,15 @@ pub struct InitilizeAccount3<'a> { impl<'a> InitilizeAccount3<'a> { #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) + pub fn invoke(&self, token_program: TokenProgramVariant) -> ProgramResult { + self.invoke_signed(&[], token_program) } - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + pub fn invoke_signed( + &self, + signers: &[Signer], + token_program: TokenProgramVariant, + ) -> ProgramResult { // account metadata let account_metas: [AccountMeta; 2] = [ AccountMeta::writable(self.token.key()), @@ -48,7 +54,7 @@ impl<'a> InitilizeAccount3<'a> { write_bytes(&mut instruction_data[1..], self.owner); let instruction = Instruction { - program_id: &crate::ID, + program_id: &token_program.into(), accounts: &account_metas, data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 33) }, }; diff --git a/programs/token/src/instructions/initialize_mint.rs b/programs/token/src/instructions/initialize_mint.rs index 3cf1fa3..5eccf72 100644 --- a/programs/token/src/instructions/initialize_mint.rs +++ b/programs/token/src/instructions/initialize_mint.rs @@ -10,6 +10,8 @@ use pinocchio::{ use crate::{write_bytes, UNINIT_BYTE}; +use super::TokenProgramVariant; + /// Initialize a new mint. /// /// ### Accounts: @@ -30,11 +32,15 @@ pub struct InitilizeMint<'a> { impl<'a> InitilizeMint<'a> { #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) + pub fn invoke(&self, token_program: TokenProgramVariant) -> ProgramResult { + self.invoke_signed(&[], token_program) } - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + pub fn invoke_signed( + &self, + signers: &[Signer], + token_program: TokenProgramVariant, + ) -> ProgramResult { // Account metadata let account_metas: [AccountMeta; 2] = [ AccountMeta::writable(self.mint.key()), @@ -64,7 +70,7 @@ impl<'a> InitilizeMint<'a> { } let instruction = Instruction { - program_id: &crate::ID, + program_id: &token_program.into(), accounts: &account_metas, data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 67) }, }; diff --git a/programs/token/src/instructions/initialize_mint_2.rs b/programs/token/src/instructions/initialize_mint_2.rs index 51954be..ed37031 100644 --- a/programs/token/src/instructions/initialize_mint_2.rs +++ b/programs/token/src/instructions/initialize_mint_2.rs @@ -10,6 +10,8 @@ use pinocchio::{ use crate::{write_bytes, UNINIT_BYTE}; +use super::TokenProgramVariant; + /// Initialize a new mint. /// /// ### Accounts: @@ -27,11 +29,15 @@ pub struct InitilizeMint2<'a> { impl<'a> InitilizeMint2<'a> { #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) + pub fn invoke(&self, token_program: TokenProgramVariant) -> ProgramResult { + self.invoke_signed(&[], token_program) } - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + pub fn invoke_signed( + &self, + signers: &[Signer], + token_program: TokenProgramVariant, + ) -> ProgramResult { // Account metadata let account_metas: [AccountMeta; 1] = [AccountMeta::writable(self.mint.key())]; @@ -58,7 +64,7 @@ impl<'a> InitilizeMint2<'a> { } let instruction = Instruction { - program_id: &crate::ID, + program_id: &token_program.into(), accounts: &account_metas, data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 67) }, }; diff --git a/programs/token/src/instructions/mint_to.rs b/programs/token/src/instructions/mint_to.rs index e0036db..11c1f61 100644 --- a/programs/token/src/instructions/mint_to.rs +++ b/programs/token/src/instructions/mint_to.rs @@ -9,6 +9,8 @@ use pinocchio::{ use crate::{write_bytes, UNINIT_BYTE}; +use super::TokenProgramVariant; + /// Mints new tokens to an account. /// /// ### Accounts: @@ -29,11 +31,15 @@ pub struct MintTo<'a> { impl<'a> MintTo<'a> { #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) + pub fn invoke(&self, token_program: TokenProgramVariant) -> ProgramResult { + self.invoke_signed(&[], token_program) } - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + pub fn invoke_signed( + &self, + signers: &[Signer], + token_program: TokenProgramVariant, + ) -> ProgramResult { // account metadata let account_metas: [AccountMeta; 3] = [ AccountMeta::writable(self.mint.key()), @@ -52,7 +58,7 @@ impl<'a> MintTo<'a> { write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes()); let instruction = Instruction { - program_id: &crate::ID, + program_id: &token_program.into(), accounts: &account_metas, data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 9) }, }; diff --git a/programs/token/src/instructions/mint_to_checked.rs b/programs/token/src/instructions/mint_to_checked.rs index 32e30b6..8ef6af5 100644 --- a/programs/token/src/instructions/mint_to_checked.rs +++ b/programs/token/src/instructions/mint_to_checked.rs @@ -9,6 +9,8 @@ use pinocchio::{ use crate::{write_bytes, UNINIT_BYTE}; +use super::TokenProgramVariant; + /// Mints new tokens to an account. /// /// ### Accounts: @@ -31,11 +33,15 @@ pub struct MintToChecked<'a> { impl<'a> MintToChecked<'a> { #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) + pub fn invoke(&self, token_program: TokenProgramVariant) -> ProgramResult { + self.invoke_signed(&[], token_program) } - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + pub fn invoke_signed( + &self, + signers: &[Signer], + token_program: TokenProgramVariant, + ) -> ProgramResult { // account metadata let account_metas: [AccountMeta; 3] = [ AccountMeta::writable(self.mint.key()), @@ -57,7 +63,7 @@ impl<'a> MintToChecked<'a> { write_bytes(&mut instruction_data[9..], &[self.decimals]); let instruction = Instruction { - program_id: &crate::ID, + program_id: &token_program.into(), accounts: &account_metas, data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 10) }, }; diff --git a/programs/token/src/instructions/mod.rs b/programs/token/src/instructions/mod.rs index 9287f8f..c5f3958 100644 --- a/programs/token/src/instructions/mod.rs +++ b/programs/token/src/instructions/mod.rs @@ -31,9 +31,29 @@ pub use initialize_mint::*; pub use initialize_mint_2::*; pub use mint_to::*; pub use mint_to_checked::*; + pub use revoke::*; pub use set_authority::*; pub use sync_native::*; pub use thaw_account::*; pub use transfer::*; pub use transfer_checked::*; + +#[repr(u8)] +#[derive(Clone, Copy)] +pub enum TokenProgramVariant { + Legacy, + Token2022, +} + +use pinocchio::pubkey::Pubkey; + +use crate::{LEGACY_TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID}; +impl Into for TokenProgramVariant { + fn into(self) -> Pubkey { + match self { + TokenProgramVariant::Legacy => LEGACY_TOKEN_PROGRAM_ID, + TokenProgramVariant::Token2022 => TOKEN_2022_PROGRAM_ID, + } + } +} diff --git a/programs/token/src/instructions/revoke.rs b/programs/token/src/instructions/revoke.rs index 75a7f6b..b0d53e8 100644 --- a/programs/token/src/instructions/revoke.rs +++ b/programs/token/src/instructions/revoke.rs @@ -5,6 +5,8 @@ use pinocchio::{ ProgramResult, }; +use super::TokenProgramVariant; + /// Revokes the delegate's authority. /// /// ### Accounts: @@ -19,11 +21,15 @@ pub struct Revoke<'a> { impl<'a> Revoke<'a> { #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) + pub fn invoke(&self, token_program: TokenProgramVariant) -> ProgramResult { + self.invoke_signed(&[], token_program) } - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + pub fn invoke_signed( + &self, + signers: &[Signer], + token_program: TokenProgramVariant, + ) -> ProgramResult { // account metadata let account_metas: [AccountMeta; 2] = [ AccountMeta::writable(self.token.key()), @@ -31,7 +37,7 @@ impl<'a> Revoke<'a> { ]; let instruction = Instruction { - program_id: &crate::ID, + program_id: &token_program.into(), accounts: &account_metas, data: &[5], }; diff --git a/programs/token/src/instructions/set_authority.rs b/programs/token/src/instructions/set_authority.rs index 2ded29b..e6d6957 100644 --- a/programs/token/src/instructions/set_authority.rs +++ b/programs/token/src/instructions/set_authority.rs @@ -10,6 +10,8 @@ use pinocchio::{ use crate::{write_bytes, UNINIT_BYTE}; +use super::TokenProgramVariant; + #[repr(u8)] #[derive(Clone, Copy)] pub enum AuthorityType { @@ -40,11 +42,15 @@ pub struct SetAuthority<'a> { impl<'a> SetAuthority<'a> { #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) + pub fn invoke(&self, token_program: TokenProgramVariant) -> ProgramResult { + self.invoke_signed(&[], token_program) } - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + pub fn invoke_signed( + &self, + signers: &[Signer], + token_program: TokenProgramVariant, + ) -> ProgramResult { // account metadata let account_metas: [AccountMeta; 2] = [ AccountMeta::writable(self.account.key()), @@ -71,7 +77,7 @@ impl<'a> SetAuthority<'a> { } let instruction = Instruction { - program_id: &crate::ID, + program_id: &token_program.into(), accounts: &account_metas, data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 35) }, }; diff --git a/programs/token/src/instructions/sync_native.rs b/programs/token/src/instructions/sync_native.rs index 46a192e..45f4612 100644 --- a/programs/token/src/instructions/sync_native.rs +++ b/programs/token/src/instructions/sync_native.rs @@ -5,6 +5,8 @@ use pinocchio::{ ProgramResult, }; +use super::TokenProgramVariant; + /// Given a native token account updates its amount field based /// on the account's underlying `lamports`. /// @@ -18,16 +20,20 @@ pub struct SyncNative<'a> { impl<'a> SyncNative<'a> { #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) + pub fn invoke(&self, token_program: TokenProgramVariant) -> ProgramResult { + self.invoke_signed(&[], token_program) } - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + pub fn invoke_signed( + &self, + signers: &[Signer], + token_program: TokenProgramVariant, + ) -> ProgramResult { // account metadata let account_metas: [AccountMeta; 1] = [AccountMeta::writable(self.native_token.key())]; let instruction = Instruction { - program_id: &crate::ID, + program_id: &token_program.into(), accounts: &account_metas, data: &[17], }; diff --git a/programs/token/src/instructions/thaw_account.rs b/programs/token/src/instructions/thaw_account.rs index 8353716..3aec1c7 100644 --- a/programs/token/src/instructions/thaw_account.rs +++ b/programs/token/src/instructions/thaw_account.rs @@ -5,6 +5,8 @@ use pinocchio::{ ProgramResult, }; +use super::TokenProgramVariant; + /// Thaw a Frozen account using the Mint's freeze_authority /// /// ### Accounts: @@ -22,11 +24,15 @@ pub struct ThawAccount<'a> { impl<'a> ThawAccount<'a> { #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) + pub fn invoke(&self, token_program: TokenProgramVariant) -> ProgramResult { + self.invoke_signed(&[], token_program) } - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + pub fn invoke_signed( + &self, + signers: &[Signer], + token_program: TokenProgramVariant, + ) -> ProgramResult { // account metadata let account_metas: [AccountMeta; 3] = [ AccountMeta::writable(self.token.key()), @@ -35,7 +41,7 @@ impl<'a> ThawAccount<'a> { ]; let instruction = Instruction { - program_id: &crate::ID, + program_id: &token_program.into(), accounts: &account_metas, data: &[11], }; diff --git a/programs/token/src/instructions/transfer.rs b/programs/token/src/instructions/transfer.rs index ad58ce1..859e8d3 100644 --- a/programs/token/src/instructions/transfer.rs +++ b/programs/token/src/instructions/transfer.rs @@ -9,6 +9,8 @@ use pinocchio::{ use crate::{write_bytes, UNINIT_BYTE}; +use super::TokenProgramVariant; + /// Transfer Tokens from one Token Account to another. /// /// ### Accounts: @@ -28,11 +30,15 @@ pub struct Transfer<'a> { impl<'a> Transfer<'a> { #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) + pub fn invoke(&self, token_program: TokenProgramVariant) -> ProgramResult { + self.invoke_signed(&[], token_program) } - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + pub fn invoke_signed( + &self, + signers: &[Signer], + token_program: TokenProgramVariant, + ) -> ProgramResult { // account metadata let account_metas: [AccountMeta; 3] = [ AccountMeta::writable(self.from.key()), @@ -51,7 +57,7 @@ impl<'a> Transfer<'a> { write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes()); let instruction = Instruction { - program_id: &crate::ID, + program_id: &token_program.into(), accounts: &account_metas, data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 9) }, }; diff --git a/programs/token/src/instructions/transfer_checked.rs b/programs/token/src/instructions/transfer_checked.rs index c4ded10..30c0088 100644 --- a/programs/token/src/instructions/transfer_checked.rs +++ b/programs/token/src/instructions/transfer_checked.rs @@ -9,6 +9,8 @@ use pinocchio::{ use crate::{write_bytes, UNINIT_BYTE}; +use super::TokenProgramVariant; + /// Transfer Tokens from one Token Account to another. /// /// ### Accounts: @@ -33,11 +35,15 @@ pub struct TransferChecked<'a> { impl<'a> TransferChecked<'a> { #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed(&[]) + pub fn invoke(&self, token_program: TokenProgramVariant) -> ProgramResult { + self.invoke_signed(&[], token_program) } - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + pub fn invoke_signed( + &self, + signers: &[Signer], + token_program: TokenProgramVariant, + ) -> ProgramResult { // account metadata let account_metas: [AccountMeta; 4] = [ AccountMeta::writable(self.from.key()), @@ -60,7 +66,7 @@ impl<'a> TransferChecked<'a> { write_bytes(&mut instruction_data[9..], &[self.decimals]); let instruction = Instruction { - program_id: &crate::ID, + program_id: &token_program.into(), accounts: &account_metas, data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 10) }, }; diff --git a/programs/token/src/lib.rs b/programs/token/src/lib.rs index ce23065..ce04e46 100644 --- a/programs/token/src/lib.rs +++ b/programs/token/src/lib.rs @@ -1,9 +1,14 @@ #![no_std] +pub mod extensions; pub mod instructions; pub mod state; -pinocchio_pubkey::declare_id!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); +use pinocchio::pubkey::Pubkey; +use pinocchio_pubkey::pubkey; + +pub const LEGACY_TOKEN_PROGRAM_ID: Pubkey = pubkey!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); +pub const TOKEN_2022_PROGRAM_ID: Pubkey = pubkey!("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"); use core::mem::MaybeUninit; diff --git a/programs/token/src/state/mint.rs b/programs/token/src/state/mint.rs index 25c1e6e..d2e42e4 100644 --- a/programs/token/src/state/mint.rs +++ b/programs/token/src/state/mint.rs @@ -4,7 +4,7 @@ use pinocchio::{ pubkey::Pubkey, }; -use crate::ID; +use crate::instructions::TokenProgramVariant; /// Mint data. #[repr(C)] @@ -43,11 +43,17 @@ impl Mint { /// This method performs owner and length validation on `AccountInfo`, safe borrowing /// the account data. #[inline] - pub fn from_account_info(account_info: &AccountInfo) -> Result, ProgramError> { + pub fn from_account_info( + account_info: &AccountInfo, + token_program: TokenProgramVariant, + ) -> Result, ProgramError> { if account_info.data_len() != Self::LEN { return Err(ProgramError::InvalidAccountData); } - if account_info.owner() != &ID { + + let token_program_id: Pubkey = token_program.into(); + + if account_info.owner() != &token_program_id { return Err(ProgramError::InvalidAccountOwner); } Ok(Ref::map(account_info.try_borrow_data()?, |data| unsafe { @@ -65,13 +71,14 @@ impl Mint { /// The caller must ensure that it is safe to borrow the account data – e.g., there are /// no mutable borrows of the account data. #[inline] - pub unsafe fn from_account_info_unchecked( - account_info: &AccountInfo, - ) -> Result<&Self, ProgramError> { + pub unsafe fn from_account_info_unchecked<'a>( + account_info: &'a AccountInfo, + token_program_id: &Pubkey, + ) -> Result<&'a Self, ProgramError> { if account_info.data_len() != Self::LEN { return Err(ProgramError::InvalidAccountData); } - if account_info.owner() != &ID { + if account_info.owner() != token_program_id { return Err(ProgramError::InvalidAccountOwner); } Ok(Self::from_bytes(account_info.borrow_data_unchecked())) diff --git a/programs/token/src/state/token.rs b/programs/token/src/state/token.rs index 2250a21..17c8e03 100644 --- a/programs/token/src/state/token.rs +++ b/programs/token/src/state/token.rs @@ -1,3 +1,5 @@ +use crate::instructions::TokenProgramVariant; + use super::AccountState; use pinocchio::{ account_info::{AccountInfo, Ref}, @@ -5,8 +7,6 @@ use pinocchio::{ pubkey::Pubkey, }; -use crate::ID; - /// Token account data. #[repr(C)] pub struct TokenAccount { @@ -58,11 +58,15 @@ impl TokenAccount { #[inline] pub fn from_account_info( account_info: &AccountInfo, + token_program: TokenProgramVariant, ) -> Result, ProgramError> { if account_info.data_len() != Self::LEN { return Err(ProgramError::InvalidAccountData); } - if account_info.owner() != &ID { + + let token_program_id: Pubkey = token_program.into(); + + if account_info.owner() != &token_program_id { return Err(ProgramError::InvalidAccountData); } Ok(Ref::map(account_info.try_borrow_data()?, |data| unsafe { @@ -82,11 +86,15 @@ impl TokenAccount { #[inline] pub unsafe fn from_account_info_unchecked( account_info: &AccountInfo, + token_program: TokenProgramVariant, ) -> Result<&TokenAccount, ProgramError> { if account_info.data_len() != Self::LEN { return Err(ProgramError::InvalidAccountData); } - if account_info.owner() != &ID { + + let token_program_id: Pubkey = token_program.into(); + + if account_info.owner() != &token_program_id { return Err(ProgramError::InvalidAccountData); } Ok(Self::from_bytes(account_info.borrow_data_unchecked())) diff --git a/programs/token2022/Cargo.toml b/programs/token2022/Cargo.toml deleted file mode 100644 index 28b6666..0000000 --- a/programs/token2022/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "pinocchio-token2022" -description = "Pinocchio helpers to invoke Token program 2022 instructions" -version = "0.2.0" -edition = { workspace = true } -license = { workspace = true } -readme = "./README.md" -repository = { workspace = true } - -[dependencies] -pinocchio = { workspace = true } -pinocchio-pubkey = { workspace = true } diff --git a/programs/token2022/README.md b/programs/token2022/README.md deleted file mode 100644 index 9bfaa09..0000000 --- a/programs/token2022/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# pinocchio-token2022 - -This crate contains [`pinocchio`](https://crates.io/crates/pinocchio) helpers to perform cross-program invocations (CPIs) for Token 2022 instructions. - -Each instruction defines an `struct` with the accounts and parameters required. Once all values are set, you can call directly `invoke` or `invoke_signed` to perform the CPI. - -This is a `no_std` crate. - -> **Note:** The API defined in this crate is subject to change. - -## Examples - -Initializing a mint account: - -```rust -// This example assumes that the instruction receives a writable `mint` -// account; `authority` is a `Pubkey`. -InitilizeMint { - mint, - rent_sysvar, - decimals: 9, - mint_authority: authority, - freeze_authority: Some(authority), -}.invoke()?; -``` - -Performing a transfer of tokens: - -```rust -// This example assumes that the instruction receives writable `from` and `to` -// accounts, and a signer `authority` account. -Transfer { - from, - to, - authority, - amount: 10, -}.invoke()?; -``` - -## License - -The code is licensed under the [Apache License Version 2.0](../LICENSE) diff --git a/programs/token2022/src/lib.rs b/programs/token2022/src/lib.rs deleted file mode 100644 index 7d37e4c..0000000 --- a/programs/token2022/src/lib.rs +++ /dev/null @@ -1,27 +0,0 @@ -#![no_std] - -pub mod extensions; -pub mod state; - -pinocchio_pubkey::declare_id!("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"); - -use core::mem::MaybeUninit; - -const UNINIT_BYTE: MaybeUninit = MaybeUninit::::uninit(); - -/// Deserialize a type from a byte array. -/// -/// # Safety -/// -/// This function is unsafe because it transmutes the input data to the output type. -pub unsafe fn from_bytes(data: &[u8]) -> T { - assert_eq!(data.len(), core::mem::size_of::()); - *(data.as_ptr() as *const T) -} - -#[inline(always)] -fn write_bytes(destination: &mut [MaybeUninit], source: &[u8]) { - for (d, s) in destination.iter_mut().zip(source.iter()) { - d.write(*s); - } -} diff --git a/programs/token2022/src/state/account_state.rs b/programs/token2022/src/state/account_state.rs deleted file mode 100644 index 5380c96..0000000 --- a/programs/token2022/src/state/account_state.rs +++ /dev/null @@ -1,36 +0,0 @@ -#[repr(u8)] -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum AccountState { - /// Account is not yet initialized - Uninitialized, - - /// Account is initialized; the account owner and/or delegate may perform - /// permitted operations on this account - Initialized, - - /// Account has been frozen by the mint freeze authority. Neither the - /// account owner nor the delegate are able to perform operations on - /// this account. - Frozen, -} - -impl From for AccountState { - fn from(value: u8) -> Self { - match value { - 0 => AccountState::Uninitialized, - 1 => AccountState::Initialized, - 2 => AccountState::Frozen, - _ => panic!("invalid account state value: {value}"), - } - } -} - -impl From for u8 { - fn from(value: AccountState) -> Self { - match value { - AccountState::Uninitialized => 0, - AccountState::Initialized => 1, - AccountState::Frozen => 2, - } - } -} diff --git a/programs/token2022/src/state/mint.rs b/programs/token2022/src/state/mint.rs deleted file mode 100644 index 25c1e6e..0000000 --- a/programs/token2022/src/state/mint.rs +++ /dev/null @@ -1,145 +0,0 @@ -use pinocchio::{ - account_info::{AccountInfo, Ref}, - program_error::ProgramError, - pubkey::Pubkey, -}; - -use crate::ID; - -/// Mint data. -#[repr(C)] -pub struct Mint { - /// Indicates whether the mint authority is present or not. - mint_authority_flag: [u8; 4], - - /// Optional authority used to mint new tokens. The mint authority may only - /// be provided during mint creation. If no mint authority is present - /// then the mint has a fixed supply and no further tokens may be - /// minted. - mint_authority: Pubkey, - - /// Total supply of tokens. - supply: [u8; 8], - - /// Number of base 10 digits to the right of the decimal place. - decimals: u8, - - /// Is `true` if this structure has been initialized. - is_initialized: u8, - - /// Indicates whether the freeze authority is present or not. - freeze_authority_flag: [u8; 4], - - /// Optional authority to freeze token accounts. - freeze_authority: Pubkey, -} - -impl Mint { - /// The length of the `Mint` account data. - pub const LEN: usize = core::mem::size_of::(); - - /// Return a `Mint` from the given account info. - /// - /// This method performs owner and length validation on `AccountInfo`, safe borrowing - /// the account data. - #[inline] - pub fn from_account_info(account_info: &AccountInfo) -> Result, ProgramError> { - if account_info.data_len() != Self::LEN { - return Err(ProgramError::InvalidAccountData); - } - if account_info.owner() != &ID { - return Err(ProgramError::InvalidAccountOwner); - } - Ok(Ref::map(account_info.try_borrow_data()?, |data| unsafe { - Self::from_bytes(data) - })) - } - - /// Return a `Mint` from the given account info. - /// - /// This method performs owner and length validation on `AccountInfo`, but does not - /// perform the borrow check. - /// - /// # Safety - /// - /// The caller must ensure that it is safe to borrow the account data – e.g., there are - /// no mutable borrows of the account data. - #[inline] - pub unsafe fn from_account_info_unchecked( - account_info: &AccountInfo, - ) -> Result<&Self, ProgramError> { - if account_info.data_len() != Self::LEN { - return Err(ProgramError::InvalidAccountData); - } - if account_info.owner() != &ID { - return Err(ProgramError::InvalidAccountOwner); - } - Ok(Self::from_bytes(account_info.borrow_data_unchecked())) - } - - /// Return a `Mint` from the given bytes. - /// - /// # Safety - /// - /// The caller must ensure that `bytes` contains a valid representation of `Mint`. - #[inline(always)] - pub unsafe fn from_bytes(bytes: &[u8]) -> &Self { - &*(bytes.as_ptr() as *const Mint) - } - - #[inline(always)] - pub fn has_mint_authority(&self) -> bool { - self.mint_authority_flag[0] == 1 - } - - pub fn mint_authority(&self) -> Option<&Pubkey> { - if self.has_mint_authority() { - Some(self.mint_authority_unchecked()) - } else { - None - } - } - - /// Return the mint authority. - /// - /// This method should be used when the caller knows that the mint will have a mint - /// authority set since it skips the `Option` check. - #[inline(always)] - pub fn mint_authority_unchecked(&self) -> &Pubkey { - &self.mint_authority - } - - pub fn supply(&self) -> u64 { - unsafe { core::ptr::read_unaligned(self.supply.as_ptr() as *const u64) } - } - - pub fn decimals(&self) -> u8 { - self.decimals - } - - pub fn is_initialized(&self) -> bool { - self.is_initialized == 1 - } - - #[inline(always)] - pub fn has_freeze_authority(&self) -> bool { - self.freeze_authority_flag[0] == 1 - } - - pub fn freeze_authority(&self) -> Option<&Pubkey> { - if self.has_freeze_authority() { - Some(self.freeze_authority_unchecked()) - } else { - None - } - } - - /// Return the freeze authority. - /// - /// This method should be used when the caller knows that the mint will have a freeze - /// authority set since it skips the `Option` check. - #[inline(always)] - pub fn freeze_authority_unchecked(&self) -> &Pubkey { - &self.freeze_authority - } -} diff --git a/programs/token2022/src/state/mod.rs b/programs/token2022/src/state/mod.rs deleted file mode 100644 index 1cbe1a1..0000000 --- a/programs/token2022/src/state/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -pub mod token; -pub use token::*; - -pub mod mint; -pub use mint::*; - -pub mod account_state; -pub use account_state::*; diff --git a/programs/token2022/src/state/token.rs b/programs/token2022/src/state/token.rs deleted file mode 100644 index 2250a21..0000000 --- a/programs/token2022/src/state/token.rs +++ /dev/null @@ -1,198 +0,0 @@ -use super::AccountState; -use pinocchio::{ - account_info::{AccountInfo, Ref}, - program_error::ProgramError, - pubkey::Pubkey, -}; - -use crate::ID; - -/// Token account data. -#[repr(C)] -pub struct TokenAccount { - /// The mint associated with this account - mint: Pubkey, - - /// The owner of this account. - owner: Pubkey, - - /// The amount of tokens this account holds. - amount: [u8; 8], - - /// Indicates whether the delegate is present or not. - delegate_flag: [u8; 4], - - /// If `delegate` is `Some` then `delegated_amount` represents - /// the amount authorized by the delegate. - delegate: Pubkey, - - /// The account's state. - state: u8, - - /// Indicates whether this account represents a native token or not. - is_native: [u8; 4], - - /// If is_native.is_some, this is a native token, and the value logs the - /// rent-exempt reserve. An Account is required to be rent-exempt, so - /// the value is used by the Processor to ensure that wrapped SOL - /// accounts do not drop below this threshold. - native_amount: [u8; 8], - - /// The amount delegated. - delegated_amount: [u8; 8], - - /// Indicates whether the close authority is present or not. - close_authority_flag: [u8; 4], - - /// Optional authority to close the account. - close_authority: Pubkey, -} - -impl TokenAccount { - pub const LEN: usize = core::mem::size_of::(); - - /// Return a `TokenAccount` from the given account info. - /// - /// This method performs owner and length validation on `AccountInfo`, safe borrowing - /// the account data. - #[inline] - pub fn from_account_info( - account_info: &AccountInfo, - ) -> Result, ProgramError> { - if account_info.data_len() != Self::LEN { - return Err(ProgramError::InvalidAccountData); - } - if account_info.owner() != &ID { - return Err(ProgramError::InvalidAccountData); - } - Ok(Ref::map(account_info.try_borrow_data()?, |data| unsafe { - Self::from_bytes(data) - })) - } - - /// Return a `TokenAccount` from the given account info. - /// - /// This method performs owner and length validation on `AccountInfo`, but does not - /// perform the borrow check. - /// - /// # Safety - /// - /// The caller must ensure that it is safe to borrow the account data – e.g., there are - /// no mutable borrows of the account data. - #[inline] - pub unsafe fn from_account_info_unchecked( - account_info: &AccountInfo, - ) -> Result<&TokenAccount, ProgramError> { - if account_info.data_len() != Self::LEN { - return Err(ProgramError::InvalidAccountData); - } - if account_info.owner() != &ID { - return Err(ProgramError::InvalidAccountData); - } - Ok(Self::from_bytes(account_info.borrow_data_unchecked())) - } - - /// Return a `TokenAccount` from the given bytes. - /// - /// # Safety - /// - /// The caller must ensure that `bytes` contains a valid representation of `TokenAccount`. - #[inline(always)] - pub unsafe fn from_bytes(bytes: &[u8]) -> &Self { - &*(bytes.as_ptr() as *const TokenAccount) - } - - pub fn mint(&self) -> &Pubkey { - &self.mint - } - - pub fn owner(&self) -> &Pubkey { - &self.owner - } - - pub fn amount(&self) -> u64 { - unsafe { core::ptr::read_unaligned(self.amount.as_ptr() as *const u64) } - } - - #[inline(always)] - pub fn has_delegate(&self) -> bool { - self.delegate_flag[0] == 1 - } - - pub fn delegate(&self) -> Option<&Pubkey> { - if self.has_delegate() { - Some(self.delegate_unchecked()) - } else { - None - } - } - - /// Use this when you know the account will have a delegate and want to skip the `Option` check. - #[inline(always)] - pub fn delegate_unchecked(&self) -> &Pubkey { - &self.delegate - } - - #[inline(always)] - pub fn state(&self) -> AccountState { - self.state.into() - } - - #[inline(always)] - pub fn is_native(&self) -> bool { - self.is_native[0] == 1 - } - - pub fn native_amount(&self) -> Option { - if self.is_native() { - Some(self.native_amount_unchecked()) - } else { - None - } - } - - /// Return the native amount. - /// - /// This method should be used when the caller knows that the token is native since it - /// skips the `Option` check. - #[inline(always)] - pub fn native_amount_unchecked(&self) -> u64 { - unsafe { core::ptr::read_unaligned(self.native_amount.as_ptr() as *const u64) } - } - - pub fn delegated_amount(&self) -> u64 { - unsafe { core::ptr::read_unaligned(self.delegated_amount.as_ptr() as *const u64) } - } - - #[inline(always)] - pub fn has_close_authority(&self) -> bool { - self.close_authority_flag[0] == 1 - } - - pub fn close_authority(&self) -> Option<&Pubkey> { - if self.has_close_authority() { - Some(self.close_authority_unchecked()) - } else { - None - } - } - - /// Return the close authority. - /// - /// This method should be used when the caller knows that the token will have a close - /// authority set since it skips the `Option` check. - #[inline(always)] - pub fn close_authority_unchecked(&self) -> &Pubkey { - &self.close_authority - } - - #[inline(always)] - pub fn is_initialized(&self) -> bool { - self.state != AccountState::Uninitialized as u8 - } - - #[inline(always)] - pub fn is_frozen(&self) -> bool { - self.state == AccountState::Frozen as u8 - } -} From 33acdf37199a76141cd80c85808b373da109b1ba Mon Sep 17 00:00:00 2001 From: NagaprasadVr Date: Fri, 13 Dec 2024 09:24:57 +0530 Subject: [PATCH 12/13] cleanup --- Cargo.lock | 2 +- programs/token/Cargo.toml | 4 ++-- programs/token/README.md | 10 +++++++--- programs/token/src/state/mint.rs | 26 +++++++++++++------------- programs/token/src/state/token.rs | 22 +++++++++++----------- 5 files changed, 34 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 00b8fdf..ffa7aa3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -53,7 +53,7 @@ dependencies = [ ] [[package]] -name = "pinocchio-token2022" +name = "pinocchio-token" version = "0.2.0" dependencies = [ "pinocchio", diff --git a/programs/token/Cargo.toml b/programs/token/Cargo.toml index 28b6666..05264fe 100644 --- a/programs/token/Cargo.toml +++ b/programs/token/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "pinocchio-token2022" -description = "Pinocchio helpers to invoke Token program 2022 instructions" +name = "pinocchio-token" +description = "Pinocchio helpers to invoke Legacy Token and Token-2022 instructions" version = "0.2.0" edition = { workspace = true } license = { workspace = true } diff --git a/programs/token/README.md b/programs/token/README.md index 9bfaa09..1e903fa 100644 --- a/programs/token/README.md +++ b/programs/token/README.md @@ -1,6 +1,6 @@ # pinocchio-token2022 -This crate contains [`pinocchio`](https://crates.io/crates/pinocchio) helpers to perform cross-program invocations (CPIs) for Token 2022 instructions. +This crate contains [`pinocchio`](https://crates.io/crates/pinocchio) helpers to perform cross-program invocations (CPIs) for Legacy and Token-2022 instructions. Each instruction defines an `struct` with the accounts and parameters required. Once all values are set, you can call directly `invoke` or `invoke_signed` to perform the CPI. @@ -21,7 +21,9 @@ InitilizeMint { decimals: 9, mint_authority: authority, freeze_authority: Some(authority), -}.invoke()?; +}.invoke( + TokenProgramVariant::Legacy, +)?; ``` Performing a transfer of tokens: @@ -34,7 +36,9 @@ Transfer { to, authority, amount: 10, -}.invoke()?; +}.invoke( + TokenProgramVariant::Token2022, +)?; ``` ## License diff --git a/programs/token/src/state/mint.rs b/programs/token/src/state/mint.rs index d2e42e4..a8e7771 100644 --- a/programs/token/src/state/mint.rs +++ b/programs/token/src/state/mint.rs @@ -4,7 +4,7 @@ use pinocchio::{ pubkey::Pubkey, }; -use crate::instructions::TokenProgramVariant; +use crate::{LEGACY_TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID}; /// Mint data. #[repr(C)] @@ -43,17 +43,14 @@ impl Mint { /// This method performs owner and length validation on `AccountInfo`, safe borrowing /// the account data. #[inline] - pub fn from_account_info( - account_info: &AccountInfo, - token_program: TokenProgramVariant, - ) -> Result, ProgramError> { + pub fn from_account_info(account_info: &AccountInfo) -> Result, ProgramError> { if account_info.data_len() != Self::LEN { return Err(ProgramError::InvalidAccountData); } - let token_program_id: Pubkey = token_program.into(); - - if account_info.owner() != &token_program_id { + if account_info.owner() != &TOKEN_2022_PROGRAM_ID + && account_info.owner() != &LEGACY_TOKEN_PROGRAM_ID + { return Err(ProgramError::InvalidAccountOwner); } Ok(Ref::map(account_info.try_borrow_data()?, |data| unsafe { @@ -71,16 +68,19 @@ impl Mint { /// The caller must ensure that it is safe to borrow the account data – e.g., there are /// no mutable borrows of the account data. #[inline] - pub unsafe fn from_account_info_unchecked<'a>( - account_info: &'a AccountInfo, - token_program_id: &Pubkey, - ) -> Result<&'a Self, ProgramError> { + pub unsafe fn from_account_info_unchecked( + account_info: &AccountInfo, + ) -> Result<&Self, ProgramError> { if account_info.data_len() != Self::LEN { return Err(ProgramError::InvalidAccountData); } - if account_info.owner() != token_program_id { + + if account_info.owner() != &TOKEN_2022_PROGRAM_ID + && account_info.owner() != &LEGACY_TOKEN_PROGRAM_ID + { return Err(ProgramError::InvalidAccountOwner); } + Ok(Self::from_bytes(account_info.borrow_data_unchecked())) } diff --git a/programs/token/src/state/token.rs b/programs/token/src/state/token.rs index 17c8e03..723551a 100644 --- a/programs/token/src/state/token.rs +++ b/programs/token/src/state/token.rs @@ -1,4 +1,4 @@ -use crate::instructions::TokenProgramVariant; +use crate::{LEGACY_TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID}; use super::AccountState; use pinocchio::{ @@ -58,17 +58,17 @@ impl TokenAccount { #[inline] pub fn from_account_info( account_info: &AccountInfo, - token_program: TokenProgramVariant, ) -> Result, ProgramError> { if account_info.data_len() != Self::LEN { return Err(ProgramError::InvalidAccountData); } - let token_program_id: Pubkey = token_program.into(); - - if account_info.owner() != &token_program_id { - return Err(ProgramError::InvalidAccountData); + if account_info.owner() != &TOKEN_2022_PROGRAM_ID + && account_info.owner() != &LEGACY_TOKEN_PROGRAM_ID + { + return Err(ProgramError::InvalidAccountOwner); } + Ok(Ref::map(account_info.try_borrow_data()?, |data| unsafe { Self::from_bytes(data) })) @@ -86,17 +86,17 @@ impl TokenAccount { #[inline] pub unsafe fn from_account_info_unchecked( account_info: &AccountInfo, - token_program: TokenProgramVariant, ) -> Result<&TokenAccount, ProgramError> { if account_info.data_len() != Self::LEN { return Err(ProgramError::InvalidAccountData); } - let token_program_id: Pubkey = token_program.into(); - - if account_info.owner() != &token_program_id { - return Err(ProgramError::InvalidAccountData); + if account_info.owner() != &TOKEN_2022_PROGRAM_ID + && account_info.owner() != &LEGACY_TOKEN_PROGRAM_ID + { + return Err(ProgramError::InvalidAccountOwner); } + Ok(Self::from_bytes(account_info.borrow_data_unchecked())) } From 78cdb1ddf8db61a2abb5a65c900ebc2777442738 Mon Sep 17 00:00:00 2001 From: Nagaprasadvr Date: Mon, 3 Mar 2025 11:45:51 +0530 Subject: [PATCH 13/13] feat: parse extension states --- .../src/extensions/confidential_transfer.rs | 2 +- programs/token/src/extensions/cpi_guard.rs | 10 +- .../src/extensions/default_account_state.rs | 44 +++- .../token/src/extensions/memo_transfer.rs | 16 +- programs/token/src/extensions/metadata.rs | 33 +++ programs/token/src/extensions/mod.rs | 217 ++++++++++++++++++ programs/token/src/extensions/transfer_fee.rs | 52 +++-- 7 files changed, 346 insertions(+), 28 deletions(-) create mode 100644 programs/token/src/extensions/metadata.rs diff --git a/programs/token/src/extensions/confidential_transfer.rs b/programs/token/src/extensions/confidential_transfer.rs index ca0f402..d348c5d 100644 --- a/programs/token/src/extensions/confidential_transfer.rs +++ b/programs/token/src/extensions/confidential_transfer.rs @@ -95,7 +95,7 @@ impl<'a> UpdateMint<'a> { let mut instruction_data = [UNINIT_BYTE; 33]; // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[1]); + write_bytes(&mut instruction_data, &[27]); // Set mint_authority as Pubkey at offset [1..33] write_bytes(&mut instruction_data[1..33], self.mint_authority); diff --git a/programs/token/src/extensions/cpi_guard.rs b/programs/token/src/extensions/cpi_guard.rs index 89ceb2b..aa3dd99 100644 --- a/programs/token/src/extensions/cpi_guard.rs +++ b/programs/token/src/extensions/cpi_guard.rs @@ -1,3 +1,5 @@ +use core::slice::from_raw_parts; + use pinocchio::{ account_info::{AccountInfo, Ref}, instruction::{AccountMeta, Instruction, Signer}, @@ -91,7 +93,7 @@ impl<'a> EnableCpiGuard<'a> { let mut instruction_data = [UNINIT_BYTE; 2]; // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data[0..1], todo!()); + write_bytes(&mut instruction_data[0..1], &[34]); // Enable the CPI guard write_bytes(&mut instruction_data[1..2], &[0]); @@ -102,6 +104,8 @@ impl<'a> EnableCpiGuard<'a> { data: unsafe { core::slice::from_raw_parts(instruction_data.as_ptr() as _, 2) }, }; + invoke_signed(&instruction, &[self.account, self.account_owner], signers)?; + Ok(()) } } @@ -130,7 +134,7 @@ impl<'a> DisableCpiGuard<'a> { let mut instruction_data = [UNINIT_BYTE; 2]; // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data[0..1], todo!()); + write_bytes(&mut instruction_data[0..1], &[34]); // Disable the CPI guard write_bytes(&mut instruction_data[1..2], &[1]); @@ -138,7 +142,7 @@ impl<'a> DisableCpiGuard<'a> { let instruction = Instruction { program_id: &TOKEN_2022_PROGRAM_ID, accounts: &account_metas, - data: unsafe { core::slice::from_raw_parts(instruction_data.as_ptr() as _, 2) }, + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 2) }, }; invoke_signed(&instruction, &[self.account, self.account_owner], signers)?; diff --git a/programs/token/src/extensions/default_account_state.rs b/programs/token/src/extensions/default_account_state.rs index 7b6fe5c..230b354 100644 --- a/programs/token/src/extensions/default_account_state.rs +++ b/programs/token/src/extensions/default_account_state.rs @@ -1,14 +1,27 @@ +use core::slice::from_raw_parts; + use pinocchio::{ account_info::{AccountInfo, Ref}, + instruction::{AccountMeta, Instruction, Signer}, + program::invoke_signed, program_error::ProgramError, + ProgramResult, }; -use crate::{state::AccountState, TOKEN_2022_PROGRAM_ID}; +use crate::{state::AccountState, write_bytes, TOKEN_2022_PROGRAM_ID, UNINIT_BYTE}; + +use super::Extension; pub struct DefaultAccountState { pub state: AccountState, } +impl Extension for DefaultAccountState { + const TYPE: super::ExtensionType = super::ExtensionType::DefaultAccountState; + const LEN: usize = Self::LEN; + const BASE_STATE: super::BaseState = super::BaseState::Mint; +} + impl DefaultAccountState { /// The length of the `MemoTranfer` account data. pub const LEN: usize = core::mem::size_of::(); @@ -72,6 +85,35 @@ pub struct InitializeDefaultAccountState<'a> { pub state: u8, } +impl<'a> InitializeDefaultAccountState<'a> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + // Account metadata + let account_metas: [AccountMeta; 1] = [AccountMeta::writable(self.mint.key())]; + + // Instruction data layout: + // - [0]: instruction discriminator (1 byte, u8) + let mut instruction_data = [UNINIT_BYTE; 1]; + + // Set discriminator as u8 at offset [0] + write_bytes(&mut instruction_data, &[28]); + + write_bytes(&mut instruction_data[1..2], &[self.state]); + + let instruction = Instruction { + program_id: &TOKEN_2022_PROGRAM_ID, + accounts: &account_metas, + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 1) }, + }; + + invoke_signed(&instruction, &[self.mint], signers) + } +} + pub struct UpdateDefaultAccountState<'a> { /// The mint to update pub mint: &'a AccountInfo, diff --git a/programs/token/src/extensions/memo_transfer.rs b/programs/token/src/extensions/memo_transfer.rs index b32d4ce..92265d8 100644 --- a/programs/token/src/extensions/memo_transfer.rs +++ b/programs/token/src/extensions/memo_transfer.rs @@ -1,3 +1,5 @@ +use core::slice::from_raw_parts; + use pinocchio::{ account_info::{AccountInfo, Ref}, instruction::{AccountMeta, Instruction, Signer}, @@ -6,12 +8,20 @@ use pinocchio::{ }; use crate::{write_bytes, TOKEN_2022_PROGRAM_ID, UNINIT_BYTE}; + +#[derive(Debug, Clone, Copy, PartialEq)] // State pub struct MemoTransfer { /// Require transfers into this account to be accompanied by a memo pub require_incoming_transfer_memos: bool, } +impl super::Extension for MemoTransfer { + const TYPE: super::ExtensionType = super::ExtensionType::MemoTransfer; + const LEN: usize = Self::LEN; + const BASE_STATE: super::BaseState = super::BaseState::Mint; +} + impl MemoTransfer { /// The length of the `MemoTranfer` account data. pub const LEN: usize = core::mem::size_of::(); @@ -95,7 +105,7 @@ impl<'a> EnableMemoTransfer<'a> { let mut instruction_data = [UNINIT_BYTE; 2]; // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data[0..1], &[todo!()]); + write_bytes(&mut instruction_data[0..1], &[30]); // Enable incoming transfer memos write_bytes(&mut instruction_data[1..2], &[0]); @@ -103,7 +113,7 @@ impl<'a> EnableMemoTransfer<'a> { let instruction = Instruction { program_id: &crate::TOKEN_2022_PROGRAM_ID, accounts: &account_metas, - data: unsafe { core::slice::from_raw_parts(instruction_data.as_ptr() as _, 2) }, + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 2) }, }; invoke_signed(&instruction, &[self.account], signers) @@ -135,7 +145,7 @@ impl<'a> DisableMemoTransfer<'a> { let mut instruction_data = [UNINIT_BYTE; 2]; // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data[0..1], &[todo!()]); + write_bytes(&mut instruction_data[0..1], &[30]); // Disable incoming transfer memos write_bytes(&mut instruction_data[1..2], &[1]); diff --git a/programs/token/src/extensions/metadata.rs b/programs/token/src/extensions/metadata.rs new file mode 100644 index 0000000..9c8ed02 --- /dev/null +++ b/programs/token/src/extensions/metadata.rs @@ -0,0 +1,33 @@ +use pinocchio::pubkey::Pubkey; + +use super::Extension; + +#[derive(Debug, Clone, Copy, PartialEq)] +/// Metadata for a token +pub struct TokenMetadata<'s> { + /// The authority that can sign to update the metadata + pub update_authority: Pubkey, + /// The associated mint, used to counter spoofing to be sure that metadata + /// belongs to a particular mint + pub mint: Pubkey, + /// The longer name of the token + pub name: &'s str, + /// The shortened symbol for the token + pub symbol: &'s str, + /// The URI pointing to richer metadata + pub uri: &'s str, + /// Any additional metadata about the token as key-value pairs. The program + /// must avoid storing the same key twice. + pub additional_metadata: &'s [(&'s str, &'s str)], +} + +impl<'t> TokenMetadata<'t> { + /// The length of the `TokenMetadata` account data. + pub const LEN: usize = core::mem::size_of::(); +} + +impl Extension for TokenMetadata<'_> { + const TYPE: super::ExtensionType = super::ExtensionType::TokenMetadata; + const LEN: usize = Self::LEN; + const BASE_STATE: super::BaseState = super::BaseState::Mint; +} diff --git a/programs/token/src/extensions/mod.rs b/programs/token/src/extensions/mod.rs index e2fcb1f..4d47f7f 100644 --- a/programs/token/src/extensions/mod.rs +++ b/programs/token/src/extensions/mod.rs @@ -1,9 +1,226 @@ +use crate::from_bytes; + pub mod confidential_transfer; pub mod cpi_guard; pub mod default_account_state; pub mod memo_transfer; +pub mod metadata; pub mod transfer_fee; pub const ELGAMAL_PUBKEY_LEN: usize = 32; pub struct ElagamalPubkey(pub [u8; ELGAMAL_PUBKEY_LEN]); + +pub const EXTENSIONS_PADDING: usize = 83; + +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum ExtensionType { + /// Used as padding if the account size would otherwise be 355, same as a + /// multisig + Uninitialized, + /// Includes transfer fee rate info and accompanying authorities to withdraw + /// and set the fee + TransferFeeConfig, + /// Includes withheld transfer fees + TransferFeeAmount, + /// Includes an optional mint close authority + MintCloseAuthority, + /// Auditor configuration for confidential transfers + ConfidentialTransferMint, + /// State for confidential transfers + ConfidentialTransferAccount, + /// Specifies the default Account::state for new Accounts + DefaultAccountState, + /// Indicates that the Account owner authority cannot be changed + ImmutableOwner, + /// Require inbound transfers to have memo + MemoTransfer, + /// Indicates that the tokens from this mint can't be transferred + NonTransferable, + /// Tokens accrue interest over time, + InterestBearingConfig, + /// Locks privileged token operations from happening via CPI + CpiGuard, + /// Includes an optional permanent delegate + PermanentDelegate, + /// Indicates that the tokens in this account belong to a non-transferable + /// mint + NonTransferableAccount, + /// Mint requires a CPI to a program implementing the "transfer hook" + /// interface + TransferHook, + /// Indicates that the tokens in this account belong to a mint with a + /// transfer hook + TransferHookAccount, + /// Includes encrypted withheld fees and the encryption public that they are + /// encrypted under + ConfidentialTransferFeeConfig, + /// Includes confidential withheld transfer fees + ConfidentialTransferFeeAmount, + /// Mint contains a pointer to another account (or the same account) that + /// holds metadata + MetadataPointer, + /// Mint contains token-metadata + TokenMetadata, + /// Mint contains a pointer to another account (or the same account) that + /// holds group configurations + GroupPointer, + /// Mint contains token group configurations + TokenGroup, + /// Mint contains a pointer to another account (or the same account) that + /// holds group member configurations + GroupMemberPointer, + /// Mint contains token group member configurations + TokenGroupMember, + /// Mint allowing the minting and burning of confidential tokens + ConfidentialMintBurn, + /// Tokens whose UI amount is scaled by a given amount + ScaledUiAmount, + /// Tokens where minting / burning / transferring can be paused + Pausable, + /// Indicates that the account belongs to a pausable mint + PausableAccount, +} + +impl ExtensionType { + fn from_bytes(val: [u8; 2]) -> Option { + let val = u16::from_le_bytes(val); + let ext = match val { + 0 => ExtensionType::Uninitialized, + 1 => ExtensionType::TransferFeeConfig, + 2 => ExtensionType::TransferFeeAmount, + 3 => ExtensionType::MintCloseAuthority, + 4 => ExtensionType::ConfidentialTransferMint, + 5 => ExtensionType::ConfidentialTransferAccount, + 6 => ExtensionType::DefaultAccountState, + 7 => ExtensionType::ImmutableOwner, + 8 => ExtensionType::MemoTransfer, + 9 => ExtensionType::NonTransferable, + 10 => ExtensionType::InterestBearingConfig, + 11 => ExtensionType::CpiGuard, + 12 => ExtensionType::PermanentDelegate, + 13 => ExtensionType::NonTransferableAccount, + 14 => ExtensionType::TransferHook, + 15 => ExtensionType::TransferHookAccount, + 16 => ExtensionType::ConfidentialTransferFeeConfig, + 17 => ExtensionType::ConfidentialTransferFeeAmount, + 18 => ExtensionType::MetadataPointer, + 19 => ExtensionType::TokenMetadata, + 20 => ExtensionType::GroupPointer, + 21 => ExtensionType::TokenGroup, + 22 => ExtensionType::GroupMemberPointer, + 23 => ExtensionType::TokenGroupMember, + 24 => ExtensionType::ConfidentialMintBurn, + 25 => ExtensionType::ScaledUiAmount, + 26 => ExtensionType::Pausable, + 27 => ExtensionType::PausableAccount, + _ => return None, + }; + Some(ext) + } +} + +pub const EXTENSION_LEN: usize = 2; +pub const EXTENSION_TYPE_LEN: usize = 2; + +pub enum BaseState { + Mint, + TokenAccount, +} + +pub trait Extension { + const TYPE: ExtensionType; + const LEN: usize; + const BASE_STATE: BaseState; +} + +pub fn get_extension_from_bytes(ext_bytes: &[u8]) -> Option { + let mut start = 0; + let end = ext_bytes.len(); + while start < end { + let ext_type_idx = start; + let ext_len_idx = ext_type_idx + 2; + let ext_data_idx = ext_len_idx + EXTENSION_LEN as usize; + + let ext_type: [u8; 2] = ext_bytes[ext_type_idx..ext_type_idx + EXTENSION_TYPE_LEN] + .try_into() + .ok()?; + let ext_type = ExtensionType::from_bytes(ext_type)?; + let ext_len: [u8; 2] = ext_bytes[ext_len_idx..ext_len_idx + EXTENSION_LEN] + .try_into() + .ok()?; + + let ext_len = u16::from_le_bytes(ext_len); + + if ext_type == T::TYPE && ext_len as usize == T::LEN { + let ext_data = &ext_bytes[ext_data_idx..ext_data_idx + T::LEN]; + return Some(unsafe { from_bytes(ext_data) }); + } + + start = start + EXTENSION_TYPE_LEN + EXTENSION_LEN + ext_len as usize; + } + None +} + +#[cfg(test)] +mod tests { + use crate::{ + extensions::{ + get_extension_from_bytes, transfer_fee::TransferFeeConfig, EXTENSIONS_PADDING, + }, + state::Mint, + }; + + #[test] + fn test_get_extension_from_bytes() { + pub const TEST_MINT_WITH_EXTENSIONS_SLICE: &[u8] = &[ + 1, 0, 0, 0, 221, 76, 72, 108, 144, 248, 182, 240, 7, 195, 4, 239, 36, 129, 248, 5, 24, + 107, 232, 253, 95, 82, 172, 209, 2, 92, 183, 155, 159, 103, 255, 33, 133, 204, 6, 44, + 35, 140, 0, 0, 6, 1, 1, 0, 0, 0, 23, 133, 50, 97, 239, 106, 184, 83, 42, 103, 240, 83, + 134, 90, 173, 49, 41, 63, 207, 7, 207, 18, 10, 181, 185, 161, 87, 6, 84, 141, 192, 43, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 0, 32, + 0, 23, 133, 50, 97, 239, 106, 184, 83, 42, 103, 240, 83, 134, 90, 173, 49, 41, 63, 207, + 7, 207, 18, 10, 181, 185, 161, 87, 6, 84, 141, 192, 43, 12, 0, 32, 0, 23, 133, 50, 97, + 239, 106, 184, 83, 42, 103, 240, 83, 134, 90, 173, 49, 41, 63, 207, 7, 207, 18, 10, + 181, 185, 161, 87, 6, 84, 141, 192, 43, 1, 0, 108, 0, 23, 133, 50, 97, 239, 106, 184, + 83, 42, 103, 240, 83, 134, 90, 173, 49, 41, 63, 207, 7, 207, 18, 10, 181, 185, 161, 87, + 6, 84, 141, 192, 43, 23, 133, 50, 97, 239, 106, 184, 83, 42, 103, 240, 83, 134, 90, + 173, 49, 41, 63, 207, 7, 207, 18, 10, 181, 185, 161, 87, 6, 84, 141, 192, 43, 0, 0, 0, + 0, 0, 0, 0, 0, 93, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 65, 0, 23, 133, 50, 97, 239, 106, 184, 83, + 42, 103, 240, 83, 134, 90, 173, 49, 41, 63, 207, 7, 207, 18, 10, 181, 185, 161, 87, 6, + 84, 141, 192, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 129, 0, 23, 133, 50, 97, 239, 106, 184, 83, 42, + 103, 240, 83, 134, 90, 173, 49, 41, 63, 207, 7, 207, 18, 10, 181, 185, 161, 87, 6, 84, + 141, 192, 43, 28, 55, 230, 67, 59, 115, 4, 221, 130, 115, 122, 228, 13, 155, 139, 243, + 196, 159, 91, 14, 108, 73, 168, 213, 51, 40, 179, 229, 6, 144, 28, 87, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 14, 0, 64, 0, 23, 133, 50, 97, 239, 106, 184, 83, 42, 103, 240, 83, 134, 90, 173, + 49, 41, 63, 207, 7, 207, 18, 10, 181, 185, 161, 87, 6, 84, 141, 192, 43, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, + 64, 0, 23, 133, 50, 97, 239, 106, 184, 83, 42, 103, 240, 83, 134, 90, 173, 49, 41, 63, + 207, 7, 207, 18, 10, 181, 185, 161, 87, 6, 84, 141, 192, 43, 23, 146, 72, 59, 108, 138, + 42, 135, 183, 71, 29, 129, 79, 149, 145, 249, 57, 92, 132, 10, 156, 227, 217, 244, 213, + 186, 125, 58, 75, 138, 116, 158, 19, 0, 174, 0, 23, 133, 50, 97, 239, 106, 184, 83, 42, + 103, 240, 83, 134, 90, 173, 49, 41, 63, 207, 7, 207, 18, 10, 181, 185, 161, 87, 6, 84, + 141, 192, 43, 23, 146, 72, 59, 108, 138, 42, 135, 183, 71, 29, 129, 79, 149, 145, 249, + 57, 92, 132, 10, 156, 227, 217, 244, 213, 186, 125, 58, 75, 138, 116, 158, 10, 0, 0, 0, + 80, 97, 121, 80, 97, 108, 32, 85, 83, 68, 5, 0, 0, 0, 80, 89, 85, 83, 68, 79, 0, 0, 0, + 104, 116, 116, 112, 115, 58, 47, 47, 116, 111, 107, 101, 110, 45, 109, 101, 116, 97, + 100, 97, 116, 97, 46, 112, 97, 120, 111, 115, 46, 99, 111, 109, 47, 112, 121, 117, 115, + 100, 95, 109, 101, 116, 97, 100, 97, 116, 97, 47, 112, 114, 111, 100, 47, 115, 111, + 108, 97, 110, 97, 47, 112, 121, 117, 115, 100, 95, 109, 101, 116, 97, 100, 97, 116, 97, + 46, 106, 115, 111, 110, 0, 0, 0, 0, + ]; + + let transfer_fee_ext = get_extension_from_bytes::( + &TEST_MINT_WITH_EXTENSIONS_SLICE[Mint::LEN + EXTENSIONS_PADDING + 1..], + ); + + assert!(transfer_fee_ext.is_some()); + } +} diff --git a/programs/token/src/extensions/transfer_fee.rs b/programs/token/src/extensions/transfer_fee.rs index ef46386..0dccbbb 100644 --- a/programs/token/src/extensions/transfer_fee.rs +++ b/programs/token/src/extensions/transfer_fee.rs @@ -11,8 +11,11 @@ use pinocchio::{ use crate::{write_bytes, TOKEN_2022_PROGRAM_ID, UNINIT_BYTE}; +use super::Extension; + /// Transfer fee configuration #[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq)] pub struct TransferFee { /// First epoch where the transfer fee takes effect pub epoch: [u8; 8], @@ -20,19 +23,16 @@ pub struct TransferFee { pub maximum_fee: [u8; 8], /// Amount of transfer collected as fees, expressed as basis points of the /// transfer amount, ie. increments of 0.01% - pub transfer_fee_basis_points: [u8; 8], + pub transfer_fee_basis_points: [u8; 2], } /// State #[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq)] pub struct TransferFeeConfig { - /// flag to indicate if the transfer fee config authority is present - pub transfer_fee_config_authority_flag: [u8; 4], /// Optional authority to set the fee pub transfer_fee_config_authority: Pubkey, - /// flag to indicate if the withdraw authority is present - pub withdraw_withheld_authority_flag: [u8; 4], /// Withdraw from mint instructions must be signed by this key pub withdraw_withheld_authority: Pubkey, /// Withheld transfer fee tokens that have been moved to the mint for @@ -100,6 +100,14 @@ impl TransferFeeConfig { } } +impl Extension for TransferFeeConfig { + const TYPE: super::ExtensionType = super::ExtensionType::TransferFeeConfig; + + const LEN: usize = Self::LEN; + + const BASE_STATE: super::BaseState = super::BaseState::Mint; +} + /// Instructions /// Initialize the transfer fee configuration for a mint. @@ -127,12 +135,12 @@ impl<'a> InitializeTransferFeeConfig<'a> { // Instruction data layout: // - [0]: instruction discriminator (1 byte, u8) // - [1..33]: mint (32 bytes, Pubkey) - // - [33]: transfer_fee_config_authority_flag (1 byte, u8) - // - [34..66]: transfer_fee_config_authority (32 bytes, Pubkey) - // - [66]: withdraw_withheld_authority_flag (1 byte, u8) - // - [67..99]: withdraw_withheld_authority (32 bytes, Pubkey) - // - [99..101]: transfer_fee_basis_points (2 bytes, u16) - // - [101..109]: maximum_fee (8 bytes, u64) + // - [33..37]: transfer_fee_config_authority_flag (4 byte, [u8;4]) + // - [37..69]: transfer_fee_config_authority (32 bytes, Pubkey) + // - [69..73]: withdraw_withheld_authority_flag (4 byte, [u8;4]) + // - [73..105]: withdraw_withheld_authority (32 bytes, Pubkey) + // - [105..107]: transfer_fee_basis_points (2 bytes, u16) + // - [107..115]: maximum_fee (8 bytes, u64) let mut instruction_data = [UNINIT_BYTE; 109]; @@ -143,31 +151,35 @@ impl<'a> InitializeTransferFeeConfig<'a> { // Set transfer_fee_config_authority COption at offset [33..37] let mut offset = 33; if let Some(transfer_fee_config_authority) = self.transfer_fee_config_authority { - write_bytes(&mut instruction_data[33..34], &[1]); + write_bytes(&mut instruction_data[33..37], &[1, 0, 0, 0]); write_bytes( - &mut instruction_data[34..66], + &mut instruction_data[37..69], transfer_fee_config_authority.as_ref(), ); - offset += 33; } else { - write_bytes(&mut instruction_data[33..34], &[0]); - offset += 1; + write_bytes(&mut instruction_data[33..37], &[0, 0, 0, 0]); + write_bytes(&mut instruction_data[37..69], &[0; 32]); } + offset += 36; if let Some(withdraw_withheld_authority) = self.withdraw_withheld_authority { - write_bytes(&mut instruction_data[offset..offset + 1], &[1]); + write_bytes(&mut instruction_data[offset..offset + 4], &[1, 0, 0, 0]); write_bytes( - &mut instruction_data[(offset + 1)..(offset + 1 + 32)], + &mut instruction_data[(offset + 4)..(offset + 4 + 32)], withdraw_withheld_authority.as_ref(), ); } else { - write_bytes(&mut instruction_data[offset..offset + 33], &[0]); + write_bytes(&mut instruction_data[offset..offset + 4], &[0, 0, 0, 0]); + write_bytes( + &mut instruction_data[(offset + 4)..(offset + 4 + 32)], + &[0; 32], + ); } let instruction = Instruction { program_id: &crate::TOKEN_2022_PROGRAM_ID, accounts: &[AccountMeta::writable(self.mint.key())], - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 109) }, + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 115) }, }; invoke_signed(&instruction, &[self.mint], signers)