11use base64:: Engine ;
22use futures_util:: StreamExt ;
3- use pyth_lazer_client:: LazerClient ;
4- use pyth_lazer_protocol:: message:: { EvmMessage , SolanaMessage } ;
3+ use pyth_lazer_client:: { AnyResponse , LazerClient } ;
4+ use pyth_lazer_protocol:: message:: {
5+ EvmMessage , LeEcdsaMessage , LeUnsignedMessage , Message , SolanaMessage ,
6+ } ;
57use pyth_lazer_protocol:: payload:: PayloadData ;
68use pyth_lazer_protocol:: router:: {
7- Chain , Channel , DeliveryFormat , FixedRate , JsonBinaryEncoding , PriceFeedId , PriceFeedProperty ,
9+ Channel , DeliveryFormat , FixedRate , Format , JsonBinaryEncoding , PriceFeedId , PriceFeedProperty ,
810 SubscriptionParams , SubscriptionParamsRepr ,
911} ;
1012use pyth_lazer_protocol:: subscription:: { Request , Response , SubscribeRequest , SubscriptionId } ;
13+ use tokio:: pin;
1114
1215fn get_lazer_access_token ( ) -> String {
1316 // Place your access token in your env at LAZER_ACCESS_TOKEN or set it here
@@ -22,7 +25,8 @@ async fn main() -> anyhow::Result<()> {
2225 "wss://pyth-lazer.dourolabs.app/v1/stream" ,
2326 & get_lazer_access_token ( ) ,
2427 ) ?;
25- let mut stream = client. start ( ) . await ?;
28+ let stream = client. start ( ) . await ?;
29+ pin ! ( stream) ;
2630
2731 let subscription_requests = vec ! [
2832 // Example subscription: Parsed JSON feed targeting Solana
@@ -36,7 +40,7 @@ async fn main() -> anyhow::Result<()> {
3640 PriceFeedProperty :: BestAskPrice ,
3741 PriceFeedProperty :: BestBidPrice ,
3842 ] ,
39- chains : vec![ Chain :: Solana ] ,
43+ formats : vec![ Format :: Solana ] ,
4044 delivery_format: DeliveryFormat :: Json ,
4145 json_binary_encoding: JsonBinaryEncoding :: Base64 ,
4246 parsed: true ,
@@ -57,7 +61,7 @@ async fn main() -> anyhow::Result<()> {
5761 PriceFeedProperty :: BestAskPrice ,
5862 PriceFeedProperty :: BestBidPrice ,
5963 ] ,
60- chains : vec![ Chain :: Evm , Chain :: Solana ] ,
64+ formats : vec![ Format :: Evm , Format :: Solana ] ,
6165 delivery_format: DeliveryFormat :: Binary ,
6266 json_binary_encoding: JsonBinaryEncoding :: Base64 ,
6367 parsed: false ,
@@ -80,49 +84,108 @@ async fn main() -> anyhow::Result<()> {
8084 while let Some ( msg) = stream. next ( ) . await {
8185 // The stream gives us base64-encoded binary messages. We need to decode, parse, and verify them.
8286 match msg? {
83- Response :: StreamUpdated ( update) => {
84- if let Some ( evm_data) = update. payload . evm {
85- // Decode binary data
86- let binary_data =
87- base64:: engine:: general_purpose:: STANDARD . decode ( & evm_data. data ) ?;
88- let evm_message = EvmMessage :: deserialize_slice ( & binary_data) ?;
89-
90- // Parse and verify the EVM message
91- let payload = parse_and_verify_evm_message ( & evm_message) ;
92- println ! ( "EVM payload: {payload:?}\n " ) ;
93- }
87+ AnyResponse :: Json ( msg) => match msg {
88+ Response :: StreamUpdated ( update) => {
89+ println ! ( "Received a JSON update for {:?}" , update. subscription_id) ;
90+ if let Some ( evm_data) = update. payload . evm {
91+ // Decode binary data
92+ let binary_data =
93+ base64:: engine:: general_purpose:: STANDARD . decode ( & evm_data. data ) ?;
94+ let evm_message = EvmMessage :: deserialize_slice ( & binary_data) ?;
95+
96+ // Parse and verify the EVM message
97+ let payload = parse_and_verify_evm_message ( & evm_message) ;
98+ println ! ( "EVM payload: {payload:?}" ) ;
99+ }
94100
95- if let Some ( solana_data) = update. payload . solana {
96- // Decode binary data
97- let binary_data =
98- base64:: engine:: general_purpose:: STANDARD . decode ( & solana_data. data ) ?;
99- let solana_message = SolanaMessage :: deserialize_slice ( & binary_data) ?;
101+ if let Some ( solana_data) = update. payload . solana {
102+ // Decode binary data
103+ let binary_data =
104+ base64:: engine:: general_purpose:: STANDARD . decode ( & solana_data. data ) ?;
105+ let solana_message = SolanaMessage :: deserialize_slice ( & binary_data) ?;
100106
101- // Parse and verify the Solana message
102- let payload = parse_and_verify_solana_message ( & solana_message) ;
103- println ! ( "Solana payload: {payload:?}\n " ) ;
104- }
107+ // Parse and verify the Solana message
108+ let payload = parse_and_verify_solana_message ( & solana_message) ;
109+ println ! ( "Solana payload: {payload:?}" ) ;
110+ }
105111
106- if let Some ( parsed) = update. payload . parsed {
107- // Parsed payloads (`parsed: true`) are already decoded and ready to use
108- for feed in parsed. price_feeds {
109- println ! (
110- "Parsed payload: {:?}: {:?} at {:?}\n " ,
111- feed. price_feed_id, feed, parsed. timestamp_us
112- ) ;
112+ if let Some ( data) = update. payload . le_ecdsa {
113+ // Decode binary data
114+ let binary_data =
115+ base64:: engine:: general_purpose:: STANDARD . decode ( & data. data ) ?;
116+ let message = LeEcdsaMessage :: deserialize_slice ( & binary_data) ?;
117+
118+ // Parse and verify the message
119+ let payload = parse_and_verify_le_ecdsa_message ( & message) ;
120+ println ! ( "LeEcdsa payload: {payload:?}" ) ;
121+ }
122+
123+ if let Some ( data) = update. payload . le_unsigned {
124+ // Decode binary data
125+ let binary_data =
126+ base64:: engine:: general_purpose:: STANDARD . decode ( & data. data ) ?;
127+ let message = LeUnsignedMessage :: deserialize_slice ( & binary_data) ?;
128+
129+ // Parse the message
130+ let payload = PayloadData :: deserialize_slice_le ( & message. payload ) ?;
131+ println ! ( "LE unsigned payload: {payload:?}" ) ;
132+ }
133+
134+ if let Some ( parsed) = update. payload . parsed {
135+ // Parsed payloads (`parsed: true`) are already decoded and ready to use
136+ for feed in parsed. price_feeds {
137+ println ! (
138+ "Parsed payload: {:?}: {:?} at {:?}" ,
139+ feed. price_feed_id, feed, parsed. timestamp_us
140+ ) ;
141+ }
142+ }
143+ }
144+ msg => println ! ( "Received non-update message: {msg:?}" ) ,
145+ } ,
146+ AnyResponse :: Binary ( msg) => {
147+ println ! ( "Received a binary update for {:?}" , msg. subscription_id) ;
148+ for message in msg. messages {
149+ match message {
150+ Message :: Evm ( message) => {
151+ // Parse and verify the EVM message
152+ let payload = parse_and_verify_evm_message ( & message) ;
153+ println ! ( "EVM payload: {payload:?}" ) ;
154+ }
155+ Message :: Solana ( message) => {
156+ // Parse and verify the Solana message
157+ let payload = parse_and_verify_solana_message ( & message) ;
158+ println ! ( "Solana payload: {payload:?}" ) ;
159+ }
160+ Message :: LeEcdsa ( message) => {
161+ let payload = parse_and_verify_le_ecdsa_message ( & message) ;
162+ println ! ( "LeEcdsa payload: {payload:?}" ) ;
163+ }
164+ Message :: LeUnsigned ( message) => {
165+ let payload = PayloadData :: deserialize_slice_le ( & message. payload ) ?;
166+ println ! ( "LeUnsigned payload: {payload:?}" ) ;
167+ }
168+ Message :: Json ( message) => {
169+ for feed in message. price_feeds {
170+ println ! (
171+ "JSON payload: {:?}: {:?} at {:?}" ,
172+ feed. price_feed_id, feed, message. timestamp_us
173+ ) ;
174+ }
175+ }
113176 }
114177 }
115178 }
116- _ => println ! ( "Received non-update message" ) ,
117179 }
180+ println ! ( ) ;
118181
119182 count += 1 ;
120183 if count >= 50 {
121184 break ;
122185 }
123186 }
124187
125- // Unsubscribe before exiting
188+ // Unsubscribe example
126189 for sub_id in [ SubscriptionId ( 1 ) , SubscriptionId ( 2 ) ] {
127190 client. unsubscribe ( sub_id) . await ?;
128191 println ! ( "Unsubscribed from {:?}" , sub_id) ;
@@ -147,12 +210,32 @@ fn parse_and_verify_solana_message(solana_message: &SolanaMessage) -> anyhow::Re
147210
148211fn parse_and_verify_evm_message ( evm_message : & EvmMessage ) -> anyhow:: Result < PayloadData > {
149212 // Recover pubkey from message
150- libsecp256k1:: recover (
213+ let public_key = libsecp256k1:: recover (
151214 & libsecp256k1:: Message :: parse ( & alloy_primitives:: keccak256 ( & evm_message. payload ) ) ,
152215 & libsecp256k1:: Signature :: parse_standard ( & evm_message. signature ) ?,
153216 & libsecp256k1:: RecoveryId :: parse ( evm_message. recovery_id ) ?,
154217 ) ?;
218+ println ! (
219+ "evm address recovered from signature: {:?}" ,
220+ hex:: encode( & alloy_primitives:: keccak256( & public_key. serialize( ) [ 1 ..] ) [ 12 ..] )
221+ ) ;
155222
156223 let payload = PayloadData :: deserialize_slice_be ( & evm_message. payload ) ?;
157224 Ok ( payload)
158225}
226+
227+ fn parse_and_verify_le_ecdsa_message ( message : & LeEcdsaMessage ) -> anyhow:: Result < PayloadData > {
228+ // Recover pubkey from message
229+ let public_key = libsecp256k1:: recover (
230+ & libsecp256k1:: Message :: parse ( & alloy_primitives:: keccak256 ( & message. payload ) ) ,
231+ & libsecp256k1:: Signature :: parse_standard ( & message. signature ) ?,
232+ & libsecp256k1:: RecoveryId :: parse ( message. recovery_id ) ?,
233+ ) ?;
234+ println ! (
235+ "evm address recovered from signature: {:?}" ,
236+ hex:: encode( & alloy_primitives:: keccak256( & public_key. serialize( ) [ 1 ..] ) [ 12 ..] )
237+ ) ;
238+
239+ let payload = PayloadData :: deserialize_slice_le ( & message. payload ) ?;
240+ Ok ( payload)
241+ }
0 commit comments