@@ -9,6 +9,7 @@ use std::mem::{
9
9
} ;
10
10
11
11
use bytemuck:: {
12
+ bytes_of,
12
13
try_from_bytes,
13
14
try_from_bytes_mut,
14
15
Pod ,
@@ -21,12 +22,21 @@ use solana_program::pubkey::Pubkey;
21
22
use solana_program:: rent:: Rent ;
22
23
23
24
use crate :: c_oracle_header:: {
25
+ cmd_add_price_t,
24
26
cmd_hdr_t,
25
27
pc_acc,
26
28
pc_map_table_t,
29
+ pc_price_t,
30
+ pc_prod_t,
31
+ pc_pub_key_t,
27
32
PC_ACCTYPE_MAPPING ,
33
+ PC_ACCTYPE_PRICE ,
34
+ PC_ACCTYPE_PRODUCT ,
28
35
PC_MAGIC ,
29
36
PC_MAP_TABLE_SIZE ,
37
+ PC_MAX_NUM_DECIMALS ,
38
+ PC_PROD_ACC_SIZE ,
39
+ PC_PTYPE_UNKNOWN ,
30
40
} ;
31
41
use crate :: error:: OracleResult ;
32
42
@@ -110,13 +120,56 @@ pub fn add_mapping(
110
120
) ?;
111
121
112
122
initialize_mapping_account ( next_mapping, hdr. ver_ ) ?;
113
- unsafe {
114
- cur_mapping
115
- . next_
116
- . k1_
117
- . copy_from_slice ( & next_mapping. key . to_bytes ( ) ) ;
123
+ pubkey_assign ( & mut cur_mapping. next_ , & next_mapping. key . to_bytes ( ) ) ;
124
+
125
+ Ok ( SUCCESS )
126
+ }
127
+
128
+ /// add a price account to a product account
129
+ /// accounts[0] funding account [signer writable]
130
+ /// accounts[1] product account to add the price account to [signer writable]
131
+ /// accounts[2] newly created price account [signer writable]
132
+ pub fn add_price (
133
+ program_id : & Pubkey ,
134
+ accounts : & [ AccountInfo ] ,
135
+ instruction_data : & [ u8 ] ,
136
+ ) -> OracleResult {
137
+ let cmd_args = load :: < cmd_add_price_t > ( instruction_data) ?;
138
+
139
+ if cmd_args. expo_ > PC_MAX_NUM_DECIMALS as i32
140
+ || cmd_args. expo_ < -( PC_MAX_NUM_DECIMALS as i32 )
141
+ || cmd_args. ptype_ == PC_PTYPE_UNKNOWN
142
+ {
143
+ return Err ( ProgramError :: InvalidArgument ) ;
118
144
}
119
145
146
+ let [ _funding_account, product_account, price_account] = match accounts {
147
+ [ x, y, z]
148
+ if valid_funding_account ( x)
149
+ && valid_signable_account ( program_id, y, PC_PROD_ACC_SIZE as usize )
150
+ && valid_signable_account ( program_id, z, size_of :: < pc_price_t > ( ) )
151
+ && valid_fresh_account ( z) =>
152
+ {
153
+ Ok ( [ x, y, z] )
154
+ }
155
+ _ => Err ( ProgramError :: InvalidArgument ) ,
156
+ } ?;
157
+
158
+ let mut product_data = load_product_account_mut ( product_account, cmd_args. ver_ ) ?;
159
+
160
+ clear_account ( price_account) ?;
161
+
162
+ let mut price_data = load_account_as_mut :: < pc_price_t > ( price_account) ?;
163
+ price_data. magic_ = PC_MAGIC ;
164
+ price_data. ver_ = cmd_args. ver_ ;
165
+ price_data. type_ = PC_ACCTYPE_PRICE ;
166
+ price_data. size_ = ( size_of :: < pc_price_t > ( ) - size_of_val ( & price_data. comp_ ) ) as u32 ;
167
+ price_data. expo_ = cmd_args. expo_ ;
168
+ price_data. ptype_ = cmd_args. ptype_ ;
169
+ pubkey_assign ( & mut price_data. prod_ , & product_account. key . to_bytes ( ) ) ;
170
+ pubkey_assign ( & mut price_data. next_ , bytes_of ( & product_data. px_acc_ ) ) ;
171
+ pubkey_assign ( & mut product_data. px_acc_ , & price_account. key . to_bytes ( ) ) ;
172
+
120
173
Ok ( SUCCESS )
121
174
}
122
175
@@ -154,7 +207,11 @@ pub fn clear_account(account: &AccountInfo) -> Result<(), ProgramError> {
154
207
155
208
/// Interpret the bytes in `data` as a value of type `T`
156
209
fn load < T : Pod > ( data : & [ u8 ] ) -> Result < & T , ProgramError > {
157
- try_from_bytes ( & data[ 0 ..size_of :: < T > ( ) ] ) . map_err ( |_| ProgramError :: InvalidArgument )
210
+ try_from_bytes (
211
+ data. get ( 0 ..size_of :: < T > ( ) )
212
+ . ok_or ( ProgramError :: InvalidArgument ) ?,
213
+ )
214
+ . map_err ( |_| ProgramError :: InvalidArgument )
158
215
}
159
216
160
217
/// Interpret the bytes in `data` as a mutable value of type `T`
@@ -191,17 +248,16 @@ fn load_mapping_account_mut<'a>(
191
248
account : & ' a AccountInfo ,
192
249
expected_version : u32 ,
193
250
) -> Result < RefMut < ' a , pc_map_table_t > , ProgramError > {
194
- let mapping_account_ref = load_account_as_mut :: < pc_map_table_t > ( account) ?;
195
- let mapping_account = * mapping_account_ref;
251
+ let mapping_data = load_account_as_mut :: < pc_map_table_t > ( account) ?;
196
252
197
253
pyth_assert (
198
- mapping_account . magic_ == PC_MAGIC
199
- && mapping_account . ver_ == expected_version
200
- && mapping_account . type_ == PC_ACCTYPE_MAPPING ,
254
+ mapping_data . magic_ == PC_MAGIC
255
+ && mapping_data . ver_ == expected_version
256
+ && mapping_data . type_ == PC_ACCTYPE_MAPPING ,
201
257
ProgramError :: InvalidArgument ,
202
258
) ?;
203
259
204
- Ok ( mapping_account_ref )
260
+ Ok ( mapping_data )
205
261
}
206
262
207
263
/// Initialize account as a new mapping account. This function will zero out any existing data in
@@ -218,3 +274,27 @@ fn initialize_mapping_account(account: &AccountInfo, version: u32) -> Result<(),
218
274
219
275
Ok ( ( ) )
220
276
}
277
+
278
+ /// Mutably borrow the data in `account` as a product account, validating that the account
279
+ /// is properly formatted. Any mutations to the returned value will be reflected in the
280
+ /// account data. Use this to read already-initialized accounts.
281
+ fn load_product_account_mut < ' a > (
282
+ account : & ' a AccountInfo ,
283
+ expected_version : u32 ,
284
+ ) -> Result < RefMut < ' a , pc_prod_t > , ProgramError > {
285
+ let product_data = load_account_as_mut :: < pc_prod_t > ( account) ?;
286
+
287
+ pyth_assert (
288
+ product_data. magic_ == PC_MAGIC
289
+ && product_data. ver_ == expected_version
290
+ && product_data. type_ == PC_ACCTYPE_PRODUCT ,
291
+ ProgramError :: InvalidArgument ,
292
+ ) ?;
293
+
294
+ Ok ( product_data)
295
+ }
296
+
297
+ // Assign pubkey bytes from source to target, fails if source is not 32 bytes
298
+ fn pubkey_assign ( target : & mut pc_pub_key_t , source : & [ u8 ] ) {
299
+ unsafe { target. k1_ . copy_from_slice ( source) }
300
+ }
0 commit comments