@@ -62,6 +62,7 @@ use crate::utils::{
6262 check_valid_writable_account,
6363 is_component_update,
6464 pubkey_assign,
65+ pubkey_clear,
6566 pubkey_equal,
6667 pubkey_is_zero,
6768 pyth_assert,
@@ -705,3 +706,72 @@ pub fn set_min_pub(
705706
706707 Ok ( ( ) )
707708}
709+
710+ /// Delete a product account and remove it from the product list of its associated mapping account.
711+ /// The deleted product account must not have any price accounts.
712+ ///
713+ /// This function allows you to delete products from non-tail mapping accounts. This ability is a
714+ /// little weird, as it allows you to construct a list of multiple mapping accounts where non-tail
715+ /// accounts have empty space. This is fine however; users should simply add new products to the
716+ /// first available spot.
717+ pub fn del_product (
718+ program_id : & Pubkey ,
719+ accounts : & [ AccountInfo ] ,
720+ instruction_data : & [ u8 ] ,
721+ ) -> ProgramResult {
722+ let [ funding_account, mapping_account, product_account] = match accounts {
723+ [ w, x, y] => Ok ( [ w, x, y] ) ,
724+ _ => Err ( ProgramError :: InvalidArgument ) ,
725+ } ?;
726+
727+ check_valid_funding_account ( funding_account) ?;
728+ check_valid_signable_account ( program_id, mapping_account, size_of :: < pc_map_table_t > ( ) ) ?;
729+ check_valid_signable_account ( program_id, product_account, PC_PROD_ACC_SIZE as usize ) ?;
730+
731+ {
732+ let cmd_args = load :: < cmd_hdr_t > ( instruction_data) ?;
733+ let mut mapping_data = load_checked :: < pc_map_table_t > ( mapping_account, cmd_args. ver_ ) ?;
734+ let product_data = load_checked :: < pc_prod_t > ( product_account, cmd_args. ver_ ) ?;
735+
736+ // This assertion is just to make the subtractions below simpler
737+ pyth_assert ( mapping_data. num_ >= 1 , ProgramError :: InvalidArgument ) ?;
738+ pyth_assert (
739+ pubkey_is_zero ( & product_data. px_acc_ ) ,
740+ ProgramError :: InvalidArgument ,
741+ ) ?;
742+
743+ let product_key = product_account. key . to_bytes ( ) ;
744+ let product_index = mapping_data
745+ . prod_
746+ . iter ( )
747+ . position ( |x| pubkey_equal ( x, & product_key) )
748+ . ok_or ( ProgramError :: InvalidArgument ) ?;
749+
750+ let num_after_removal: usize = try_convert (
751+ mapping_data
752+ . num_
753+ . checked_sub ( 1 )
754+ . ok_or ( ProgramError :: InvalidArgument ) ?,
755+ ) ?;
756+
757+ let last_key_bytes = mapping_data. prod_ [ num_after_removal] ;
758+ pubkey_assign (
759+ & mut mapping_data. prod_ [ product_index] ,
760+ bytes_of ( & last_key_bytes) ,
761+ ) ;
762+ pubkey_clear ( & mut mapping_data. prod_ [ num_after_removal] ) ;
763+ mapping_data. num_ = try_convert :: < _ , u32 > ( num_after_removal) ?;
764+ mapping_data. size_ =
765+ try_convert :: < _ , u32 > ( size_of :: < pc_map_table_t > ( ) - size_of_val ( & mapping_data. prod_ ) ) ?
766+ + mapping_data. num_ * try_convert :: < _ , u32 > ( size_of :: < pc_pub_key_t > ( ) ) ?;
767+ }
768+
769+ // Zero out the balance of the price account to delete it.
770+ // Note that you can't use the system program's transfer instruction to do this operation, as
771+ // that instruction fails if the source account has any data.
772+ let lamports = product_account. lamports ( ) ;
773+ * * product_account. lamports . borrow_mut ( ) = 0 ;
774+ * * funding_account. lamports . borrow_mut ( ) += lamports;
775+
776+ Ok ( ( ) )
777+ }
0 commit comments