@@ -7,6 +7,8 @@ use std::{
77 str:: FromStr ,
88} ;
99
10+ use log:: info;
11+
1012use serde:: {
1113 de:: Error ,
1214 Deserialize ,
@@ -16,8 +18,10 @@ use serde::{
1618} ;
1719use solana_program:: pubkey:: Pubkey ;
1820
21+ use crate :: BatchState ;
22+
1923/// Pyth2wormhole config specific to attestation requests
20- #[ derive( Clone , Debug , Deserialize , Serialize , PartialEq ) ]
24+ #[ derive( Clone , Debug , Hash , Deserialize , Serialize , PartialEq ) ]
2125pub struct AttestationConfig {
2226 #[ serde( default = "default_min_msg_reuse_interval_ms" ) ]
2327 pub min_msg_reuse_interval_ms : u64 ,
@@ -30,6 +34,15 @@ pub struct AttestationConfig {
3034 default // Uses Option::default() which is None
3135 ) ]
3236 pub mapping_addr : Option < Pubkey > ,
37+ /// The known symbol list will be reloaded based off this
38+ /// interval, to account for mapping changes. Note: This interval
39+ /// will only work if the mapping address is defined. Whenever
40+ /// it's time to look up the mapping, new attestation jobs are
41+ /// started lazily, only if mapping contents affected the known
42+ /// symbol list, and before stopping the pre-existing obsolete
43+ /// jobs to maintain uninterrupted cranking.
44+ #[ serde( default = "default_mapping_reload_interval_mins" ) ]
45+ pub mapping_reload_interval_mins : u64 ,
3346 #[ serde( default = "default_min_rpc_interval_ms" ) ]
3447 /// Rate-limiting minimum delay between RPC requests in milliseconds"
3548 pub min_rpc_interval_ms : u64 ,
@@ -49,7 +62,7 @@ impl AttestationConfig {
4962 for existing_group in & self . symbol_groups {
5063 for existing_sym in & existing_group. symbols {
5164 // Check if new symbols mention this product
52- if let Some ( mut prices) = new_symbols. get_mut ( & existing_sym. product_addr ) {
65+ if let Some ( prices) = new_symbols. get_mut ( & existing_sym. product_addr ) {
5366 // Prune the price if exists
5467 prices. remove ( & existing_sym. price_addr ) ;
5568 }
@@ -74,7 +87,7 @@ impl AttestationConfig {
7487 . iter_mut ( )
7588 . find ( |g| g. group_name == group_name) // Advances the iterator and returns Some(item) on first hit
7689 {
77- Some ( mut existing_group) => existing_group. symbols . append ( & mut new_symbols_vec) ,
90+ Some ( existing_group) => existing_group. symbols . append ( & mut new_symbols_vec) ,
7891 None if new_symbols_vec. len ( ) != 0 => {
7992 // Group does not exist, assume defaults
8093 let new_group = SymbolGroup {
@@ -88,9 +101,30 @@ impl AttestationConfig {
88101 None => { }
89102 }
90103 }
104+
105+ pub fn as_batches ( & self , max_batch_size : usize ) -> Vec < BatchState > {
106+ self . symbol_groups
107+ . iter ( )
108+ . map ( move |g| {
109+ let conditions4closure = g. conditions . clone ( ) ;
110+ let name4closure = g. group_name . clone ( ) ;
111+
112+ info ! ( "Group {:?}, {} symbols" , g. group_name, g. symbols. len( ) , ) ;
113+
114+ // Divide group into batches
115+ g. symbols
116+ . as_slice ( )
117+ . chunks ( max_batch_size. clone ( ) )
118+ . map ( move |symbols| {
119+ BatchState :: new ( name4closure. clone ( ) , symbols, conditions4closure. clone ( ) )
120+ } )
121+ } )
122+ . flatten ( )
123+ . collect ( )
124+ }
91125}
92126
93- #[ derive( Clone , Debug , Deserialize , Serialize , PartialEq ) ]
127+ #[ derive( Clone , Debug , Hash , Deserialize , Serialize , PartialEq ) ]
94128pub struct SymbolGroup {
95129 pub group_name : String ,
96130 /// Attestation conditions applied to all symbols in this group
@@ -106,6 +140,10 @@ pub const fn default_min_msg_reuse_interval_ms() -> u64 {
106140 10_000 // 10s
107141}
108142
143+ pub const fn default_mapping_reload_interval_mins ( ) -> u64 {
144+ 15
145+ }
146+
109147pub const fn default_min_rpc_interval_ms ( ) -> u64 {
110148 150
111149}
@@ -122,7 +160,7 @@ pub const fn default_max_batch_jobs() -> usize {
122160/// of the active conditions is met. Option<> fields can be
123161/// de-activated with None. All conditions are inactive by default,
124162/// except for the non-Option ones.
125- #[ derive( Clone , Debug , Deserialize , Serialize , PartialEq ) ]
163+ #[ derive( Clone , Debug , Hash , Deserialize , Serialize , PartialEq ) ]
126164pub struct AttestationConditions {
127165 /// Baseline, unconditional attestation interval. Attestation is triggered if the specified interval elapsed since last attestation.
128166 #[ serde( default = "default_min_interval_secs" ) ]
@@ -134,9 +172,10 @@ pub struct AttestationConditions {
134172 #[ serde( default = "default_max_batch_jobs" ) ]
135173 pub max_batch_jobs : usize ,
136174
137- /// Trigger attestation if price changes by the specified percentage.
175+ /// Trigger attestation if price changes by the specified
176+ /// percentage, expressed in integer basis points (1bps = 0.01%)
138177 #[ serde( default ) ]
139- pub price_changed_pct : Option < f64 > ,
178+ pub price_changed_bps : Option < u64 > ,
140179
141180 /// Trigger attestation if publish_time advances at least the
142181 /// specified amount.
@@ -152,11 +191,11 @@ impl AttestationConditions {
152191 let AttestationConditions {
153192 min_interval_secs : _min_interval_secs,
154193 max_batch_jobs : _max_batch_jobs,
155- price_changed_pct ,
194+ price_changed_bps ,
156195 publish_time_min_delta_secs,
157196 } = self ;
158197
159- price_changed_pct . is_some ( ) || publish_time_min_delta_secs. is_some ( )
198+ price_changed_bps . is_some ( ) || publish_time_min_delta_secs. is_some ( )
160199 }
161200}
162201
@@ -165,14 +204,14 @@ impl Default for AttestationConditions {
165204 Self {
166205 min_interval_secs : default_min_interval_secs ( ) ,
167206 max_batch_jobs : default_max_batch_jobs ( ) ,
168- price_changed_pct : None ,
207+ price_changed_bps : None ,
169208 publish_time_min_delta_secs : None ,
170209 }
171210 }
172211}
173212
174213/// Config entry for a Pyth product + price pair
175- #[ derive( Clone , Default , Debug , Deserialize , Serialize , PartialEq , Eq ) ]
214+ #[ derive( Clone , Default , Debug , Hash , Deserialize , Serialize , PartialEq , Eq ) ]
176215pub struct P2WSymbol {
177216 /// User-defined human-readable name
178217 pub name : Option < String > ,
@@ -283,6 +322,7 @@ mod tests {
283322 max_msg_accounts : 100_000 ,
284323 min_rpc_interval_ms : 2123 ,
285324 mapping_addr : None ,
325+ mapping_reload_interval_mins : 42 ,
286326 symbol_groups : vec ! [ fastbois, slowbois] ,
287327 } ;
288328
@@ -302,6 +342,7 @@ mod tests {
302342 max_msg_accounts : 100 ,
303343 min_rpc_interval_ms : 42422 ,
304344 mapping_addr : None ,
345+ mapping_reload_interval_mins : 42 ,
305346 symbol_groups : vec ! [ ] ,
306347 } ;
307348
0 commit comments