1
- import { HexString } from "@pythnetwork/price-service-sdk" ;
1
+ import { HexString , Price , PriceFeed } from "@pythnetwork/price-service-sdk" ;
2
2
import cors from "cors" ;
3
3
import express , { NextFunction , Request , Response } from "express" ;
4
4
import { Joi , schema , validate , ValidationError } from "express-validation" ;
5
5
import { Server } from "http" ;
6
6
import { StatusCodes } from "http-status-codes" ;
7
7
import morgan from "morgan" ;
8
8
import fetch from "node-fetch" ;
9
- import {
10
- parseBatchPriceAttestation ,
11
- priceAttestationToPriceFeed ,
12
- } from "@pythnetwork/wormhole-attester-sdk" ;
9
+ import { parseBatchPriceAttestation } from "@pythnetwork/wormhole-attester-sdk" ;
13
10
import { removeLeading0x , TimestampInSec } from "./helpers" ;
14
- import { createPriceInfo , PriceInfo , PriceStore , VaaConfig } from "./listen" ;
11
+ import { createPriceInfo , PriceInfo , PriceStore } from "./listen" ;
15
12
import { logger } from "./logging" ;
16
13
import { PromClient } from "./promClient" ;
17
14
import { retry } from "ts-retry-promise" ;
@@ -21,7 +18,6 @@ import {
21
18
TargetChain ,
22
19
validTargetChains ,
23
20
defaultTargetChain ,
24
- VaaEncoding ,
25
21
encodeVaaForChain ,
26
22
} from "./encoding" ;
27
23
@@ -136,7 +132,128 @@ export class RestAPI {
136
132
return vaa ;
137
133
}
138
134
139
- vaaToPriceInfo ( priceFeedId : string , vaa : Buffer ) : PriceInfo | undefined {
135
+ // Extract the price info from an Accumulator update. This is a temporary solution until hermes adoption
136
+ // to maintain backward compatibility when the db migrates to the new update format.
137
+ static extractPriceInfoFromAccumulatorUpdate (
138
+ priceFeedId : string ,
139
+ updateData : Buffer
140
+ ) : PriceInfo | undefined {
141
+ let offset = 0 ;
142
+ offset += 4 ; // magic
143
+ offset += 1 ; // major version
144
+ offset += 1 ; // minor version
145
+
146
+ const trailingHeaderSize = updateData . readUint8 ( offset ) ;
147
+ offset += 1 + trailingHeaderSize ;
148
+
149
+ const updateType = updateData . readUint8 ( offset ) ;
150
+ offset += 1 ;
151
+
152
+ // There is a single update type of 0 for now.
153
+ if ( updateType !== 0 ) {
154
+ logger . error ( `Invalid accumulator update type: ${ updateType } ` ) ;
155
+ return undefined ;
156
+ }
157
+
158
+ const vaaLength = updateData . readUint16BE ( offset ) ;
159
+ offset += 2 ;
160
+
161
+ const vaaBuffer = updateData . slice ( offset , offset + vaaLength ) ;
162
+ const vaa = parseVaa ( vaaBuffer ) ;
163
+ offset += vaaLength ;
164
+
165
+ const numUpdates = updateData . readUint8 ( offset ) ;
166
+ offset += 1 ;
167
+
168
+ // Iterate through the updates to find the price info with the given id
169
+ for ( let i = 0 ; i < numUpdates ; i ++ ) {
170
+ const messageLength = updateData . readUint16BE ( offset ) ;
171
+ offset += 2 ;
172
+
173
+ const message = updateData . slice ( offset , offset + messageLength ) ;
174
+ offset += messageLength ;
175
+
176
+ const proofLength = updateData . readUint8 ( offset ) ;
177
+ offset += 1 ;
178
+
179
+ // ignore proofs
180
+ offset += proofLength ;
181
+
182
+ // Checket whether the message is a price feed update
183
+ // from the given price id and if so, extract the price info
184
+ let messageOffset = 0 ;
185
+ const messageType = message . readUint8 ( messageOffset ) ;
186
+ messageOffset += 1 ;
187
+
188
+ // MessageType of 0 is a price feed update
189
+ if ( messageType !== 0 ) {
190
+ continue ;
191
+ }
192
+
193
+ const priceId = message
194
+ . slice ( messageOffset , messageOffset + 32 )
195
+ . toString ( "hex" ) ;
196
+ messageOffset += 32 ;
197
+
198
+ if ( priceId !== priceFeedId ) {
199
+ continue ;
200
+ }
201
+
202
+ const price = message . readBigInt64BE ( messageOffset ) ;
203
+ messageOffset += 8 ;
204
+ const conf = message . readBigUint64BE ( messageOffset ) ;
205
+ messageOffset += 8 ;
206
+ const expo = message . readInt32BE ( messageOffset ) ;
207
+ messageOffset += 4 ;
208
+ const publishTime = message . readBigInt64BE ( messageOffset ) ;
209
+ messageOffset += 8 ;
210
+ const prevPublishTime = message . readBigInt64BE ( messageOffset ) ;
211
+ messageOffset += 8 ;
212
+ const emaPrice = message . readBigInt64BE ( messageOffset ) ;
213
+ messageOffset += 8 ;
214
+ const emaConf = message . readBigUint64BE ( messageOffset ) ;
215
+
216
+ return {
217
+ priceFeed : new PriceFeed ( {
218
+ id : priceFeedId ,
219
+ price : new Price ( {
220
+ price : price . toString ( ) ,
221
+ conf : conf . toString ( ) ,
222
+ expo,
223
+ publishTime : Number ( publishTime ) ,
224
+ } ) ,
225
+ emaPrice : new Price ( {
226
+ price : emaPrice . toString ( ) ,
227
+ conf : emaConf . toString ( ) ,
228
+ expo,
229
+ publishTime : Number ( publishTime ) ,
230
+ } ) ,
231
+ } ) ,
232
+ publishTime : Number ( publishTime ) ,
233
+ vaa : vaaBuffer ,
234
+ seqNum : Number ( vaa . sequence ) ,
235
+ emitterChainId : vaa . emitterChain ,
236
+ // These are not available in the accumulator update format
237
+ // but are required by the PriceInfo type.
238
+ attestationTime : Number ( publishTime ) ,
239
+ lastAttestedPublishTime : Number ( prevPublishTime ) ,
240
+ priceServiceReceiveTime : Number ( publishTime ) ,
241
+ } ;
242
+ }
243
+
244
+ return undefined ;
245
+ }
246
+
247
+ static vaaToPriceInfo (
248
+ priceFeedId : string ,
249
+ vaa : Buffer
250
+ ) : PriceInfo | undefined {
251
+ // Vaa could be the update data from the db with the Accumulator format.
252
+ const ACCUMULATOR_MAGIC = "504e4155" ;
253
+ if ( vaa . slice ( 0 , 4 ) . toString ( "hex" ) === ACCUMULATOR_MAGIC ) {
254
+ return RestAPI . extractPriceInfoFromAccumulatorUpdate ( priceFeedId , vaa ) ;
255
+ }
256
+
140
257
const parsedVaa = parseVaa ( vaa ) ;
141
258
142
259
let batchAttestation ;
@@ -454,7 +571,7 @@ export class RestAPI {
454
571
throw RestException . VaaNotFound ( ) ;
455
572
}
456
573
457
- const priceInfo = this . vaaToPriceInfo (
574
+ const priceInfo = RestAPI . vaaToPriceInfo (
458
575
priceFeedId ,
459
576
Buffer . from ( vaa . vaa , "base64" )
460
577
) ;
0 commit comments