@@ -20,12 +20,21 @@ use solana_program::pubkey::Pubkey;
2020use solana_program:: rent:: Rent ;
2121
2222use crate :: c_oracle_header:: {
23+ cmd_add_price_t,
2324 cmd_hdr_t,
2425 pc_acc,
2526 pc_map_table_t,
27+ pc_price_t,
28+ pc_prod_t,
29+ pc_pub_key_t,
2630 PC_ACCTYPE_MAPPING ,
31+ PC_ACCTYPE_PRICE ,
32+ PC_ACCTYPE_PRODUCT ,
2733 PC_MAGIC ,
2834 PC_MAP_TABLE_SIZE ,
35+ PC_MAX_NUM_DECIMALS ,
36+ PC_PROD_ACC_SIZE ,
37+ PC_PTYPE_UNKNOWN ,
2938} ;
3039use crate :: error:: OracleResult ;
3140
@@ -109,13 +118,56 @@ pub fn add_mapping(
109118 ) ?;
110119
111120 initialize_mapping_account ( next_mapping, hdr. ver_ ) ?;
112- unsafe {
113- cur_mapping
114- . next_
115- . k1_
116- . copy_from_slice ( & next_mapping. key . to_bytes ( ) ) ;
121+ pubkey_assign ( & mut cur_mapping. next_ , & next_mapping. key . to_bytes ( ) ) ;
122+
123+ Ok ( SUCCESS )
124+ }
125+
126+ /// add a price account to a product account
127+ /// accounts[0] funding account [signer writable]
128+ /// accounts[1] product account to add the price account to [signer writable]
129+ /// accounts[2] newly created price account [signer writable]
130+ pub fn add_price (
131+ program_id : & Pubkey ,
132+ accounts : & [ AccountInfo ] ,
133+ instruction_data : & [ u8 ] ,
134+ ) -> OracleResult {
135+ let cmd_args = load :: < cmd_add_price_t > ( instruction_data) ?;
136+
137+ if cmd_args. expo_ > PC_MAX_NUM_DECIMALS as i32
138+ || cmd_args. expo_ < -( PC_MAX_NUM_DECIMALS as i32 )
139+ || cmd_args. ptype_ == PC_PTYPE_UNKNOWN
140+ {
141+ return Err ( ProgramError :: InvalidArgument ) ;
117142 }
118143
144+ let [ _funding_account, product_account, price_account] = match accounts {
145+ [ x, y, z]
146+ if valid_funding_account ( x)
147+ && valid_signable_account ( program_id, y, PC_PROD_ACC_SIZE as usize )
148+ && valid_signable_account ( program_id, z, size_of :: < pc_price_t > ( ) )
149+ && valid_fresh_account ( z) =>
150+ {
151+ Ok ( [ x, y, z] )
152+ }
153+ _ => Err ( ProgramError :: InvalidArgument ) ,
154+ } ?;
155+
156+ let mut product_data = load_product_account_mut ( product_account, cmd_args. ver_ ) ?;
157+
158+ clear_account ( price_account) ?;
159+
160+ let mut price_data = load_account_as_mut :: < pc_price_t > ( price_account) ?;
161+ price_data. magic_ = PC_MAGIC ;
162+ price_data. ver_ = cmd_args. ver_ ;
163+ price_data. type_ = PC_ACCTYPE_PRICE ;
164+ price_data. size_ = ( size_of :: < pc_price_t > ( ) - size_of_val ( & price_data. comp_ ) ) as u32 ;
165+ price_data. expo_ = cmd_args. expo_ ;
166+ price_data. ptype_ = cmd_args. ptype_ ;
167+ pubkey_assign ( & mut price_data. prod_ , & product_account. key . to_bytes ( ) ) ;
168+ pubkey_assign ( & mut price_data. next_ , bytes_of ( & product_data. px_acc_ ) ) ;
169+ pubkey_assign ( & mut product_data. px_acc_ , & price_account. key . to_bytes ( ) ) ;
170+
119171 Ok ( SUCCESS )
120172}
121173
@@ -159,17 +211,16 @@ fn load_mapping_account_mut<'a>(
159211 account : & ' a AccountInfo ,
160212 expected_version : u32 ,
161213) -> Result < RefMut < ' a , pc_map_table_t > , ProgramError > {
162- let mapping_account_ref = load_account_as_mut :: < pc_map_table_t > ( account) ?;
163- let mapping_account = * mapping_account_ref;
214+ let mapping_data = load_account_as_mut :: < pc_map_table_t > ( account) ?;
164215
165216 pyth_assert (
166- mapping_account . magic_ == PC_MAGIC
167- && mapping_account . ver_ == expected_version
168- && mapping_account . type_ == PC_ACCTYPE_MAPPING ,
217+ mapping_data . magic_ == PC_MAGIC
218+ && mapping_data . ver_ == expected_version
219+ && mapping_data . type_ == PC_ACCTYPE_MAPPING ,
169220 ProgramError :: InvalidArgument ,
170221 ) ?;
171222
172- Ok ( mapping_account_ref )
223+ Ok ( mapping_data )
173224}
174225
175226/// Initialize account as a new mapping account. This function will zero out any existing data in
@@ -186,3 +237,27 @@ fn initialize_mapping_account(account: &AccountInfo, version: u32) -> Result<(),
186237
187238 Ok ( ( ) )
188239}
240+
241+ /// Mutably borrow the data in `account` as a product account, validating that the account
242+ /// is properly formatted. Any mutations to the returned value will be reflected in the
243+ /// account data. Use this to read already-initialized accounts.
244+ fn load_product_account_mut < ' a > (
245+ account : & ' a AccountInfo ,
246+ expected_version : u32 ,
247+ ) -> Result < RefMut < ' a , pc_prod_t > , ProgramError > {
248+ let product_data = load_account_as_mut :: < pc_prod_t > ( account) ?;
249+
250+ pyth_assert (
251+ product_data. magic_ == PC_MAGIC
252+ && product_data. ver_ == expected_version
253+ && product_data. type_ == PC_ACCTYPE_PRODUCT ,
254+ ProgramError :: InvalidArgument ,
255+ ) ?;
256+
257+ Ok ( product_data)
258+ }
259+
260+ // Assign pubkey bytes from source to target, fails if source is not 32 bytes
261+ fn pubkey_assign ( target : & mut pc_pub_key_t , source : & [ u8 ] ) {
262+ unsafe { target. k1_ . copy_from_slice ( source) }
263+ }
0 commit comments