@@ -73,6 +73,20 @@ abstract contract PythTestUtils is Test, WormholeTestUtils, RandTestUtils {
7373 uint64 emaConf;
7474 }
7575
76+ struct TwapPriceFeedMessage {
77+ bytes32 priceId;
78+ uint64 startTime;
79+ uint64 endTime;
80+ int64 price;
81+ uint64 conf;
82+ int32 expo;
83+ uint64 publishTime;
84+ uint64 prevPublishTime;
85+ int64 emaPrice;
86+ uint64 emaConf;
87+ uint64 downSlotsRatio;
88+ }
89+
7690 struct MerkleUpdateConfig {
7791 uint8 depth;
7892 uint8 numSigners;
@@ -101,16 +115,37 @@ abstract contract PythTestUtils is Test, WormholeTestUtils, RandTestUtils {
101115 }
102116 }
103117
104- function generateWhMerkleUpdateWithSource (
105- PriceFeedMessage[] memory priceFeedMessages ,
106- MerkleUpdateConfig memory config
107- ) internal returns (bytes memory whMerkleUpdateData ) {
108- bytes [] memory encodedPriceFeedMessages = encodePriceFeedMessages (
109- priceFeedMessages
118+ function encodeTwapPriceFeedMessages (
119+ TwapPriceFeedMessage[] memory twapPriceFeedMessages
120+ ) internal pure returns (bytes [] memory encodedTwapPriceFeedMessages ) {
121+ encodedTwapPriceFeedMessages = new bytes [](
122+ twapPriceFeedMessages.length
110123 );
111124
125+ for (uint i = 0 ; i < twapPriceFeedMessages.length ; i++ ) {
126+ encodedTwapPriceFeedMessages[i] = abi.encodePacked (
127+ uint8 (PythAccumulator.MessageType.TwapPriceFeed),
128+ twapPriceFeedMessages[i].priceId,
129+ twapPriceFeedMessages[i].startTime,
130+ twapPriceFeedMessages[i].endTime,
131+ twapPriceFeedMessages[i].price,
132+ twapPriceFeedMessages[i].conf,
133+ twapPriceFeedMessages[i].expo,
134+ twapPriceFeedMessages[i].publishTime,
135+ twapPriceFeedMessages[i].prevPublishTime,
136+ twapPriceFeedMessages[i].emaPrice,
137+ twapPriceFeedMessages[i].emaConf,
138+ twapPriceFeedMessages[i].downSlotsRatio
139+ );
140+ }
141+ }
142+
143+ function generateMerkleUpdateWithEncodedMessages (
144+ bytes [] memory encodedMessages ,
145+ MerkleUpdateConfig memory config
146+ ) internal returns (bytes memory whMerkleUpdateData ) {
112147 (bytes20 rootDigest , bytes [] memory proofs ) = MerkleTree
113- .constructProofs (encodedPriceFeedMessages , config.depth);
148+ .constructProofs (encodedMessages , config.depth);
114149
115150 bytes memory wormholePayload = abi.encodePacked (
116151 uint32 (0x41555756 ), // PythAccumulator.ACCUMULATOR_WORMHOLE_MAGIC
@@ -146,14 +181,14 @@ abstract contract PythTestUtils is Test, WormholeTestUtils, RandTestUtils {
146181 uint8 (PythAccumulator.UpdateType.WormholeMerkle),
147182 uint16 (wormholeMerkleVaa.length ),
148183 wormholeMerkleVaa,
149- uint8 (priceFeedMessages .length )
184+ uint8 (encodedMessages .length )
150185 );
151186
152- for (uint i = 0 ; i < priceFeedMessages .length ; i++ ) {
187+ for (uint i = 0 ; i < encodedMessages .length ; i++ ) {
153188 whMerkleUpdateData = abi.encodePacked (
154189 whMerkleUpdateData,
155- uint16 (encodedPriceFeedMessages [i].length ),
156- encodedPriceFeedMessages [i],
190+ uint16 (encodedMessages [i].length ),
191+ encodedMessages [i],
157192 proofs[i]
158193 );
159194 }
@@ -164,8 +199,32 @@ abstract contract PythTestUtils is Test, WormholeTestUtils, RandTestUtils {
164199 uint8 depth ,
165200 uint8 numSigners
166201 ) internal returns (bytes memory whMerkleUpdateData ) {
167- whMerkleUpdateData = generateWhMerkleUpdateWithSource (
168- priceFeedMessages,
202+ bytes [] memory encodedPriceFeedMessages = encodePriceFeedMessages (
203+ priceFeedMessages
204+ );
205+ whMerkleUpdateData = generateMerkleUpdateWithEncodedMessages (
206+ encodedPriceFeedMessages,
207+ MerkleUpdateConfig (
208+ depth,
209+ numSigners,
210+ SOURCE_EMITTER_CHAIN_ID,
211+ SOURCE_EMITTER_ADDRESS,
212+ false
213+ )
214+ );
215+ }
216+
217+ function generateTwapWhMerkleUpdate (
218+ TwapPriceFeedMessage[] memory twapPriceFeedMessages ,
219+ uint8 depth ,
220+ uint8 numSigners
221+ ) internal returns (bytes memory whMerkleUpdateData ) {
222+ bytes []
223+ memory encodedTwapPriceFeedMessages = encodeTwapPriceFeedMessages (
224+ twapPriceFeedMessages
225+ );
226+ whMerkleUpdateData = generateMerkleUpdateWithEncodedMessages (
227+ encodedTwapPriceFeedMessages,
169228 MerkleUpdateConfig (
170229 depth,
171230 numSigners,
@@ -186,7 +245,6 @@ abstract contract PythTestUtils is Test, WormholeTestUtils, RandTestUtils {
186245 bytes [] memory encodedPriceFeedMessages = encodePriceFeedMessages (
187246 priceFeedMessages
188247 );
189-
190248 (bytes20 rootDigest , bytes [] memory proofs ) = MerkleTree
191249 .constructProofs (encodedPriceFeedMessages, depth);
192250 // refactoring some of these generateWormhole functions was necessary
@@ -219,6 +277,46 @@ abstract contract PythTestUtils is Test, WormholeTestUtils, RandTestUtils {
219277 }
220278 }
221279
280+ function generateForwardCompatibleTwapWhMerkleUpdate (
281+ TwapPriceFeedMessage[] memory twapPriceFeedMessages ,
282+ uint8 depth ,
283+ uint8 numSigners ,
284+ uint8 minorVersion ,
285+ bytes memory trailingHeaderData
286+ ) internal view returns (bytes memory whMerkleUpdateData ) {
287+ bytes []
288+ memory encodedTwapPriceFeedMessages = encodeTwapPriceFeedMessages (
289+ twapPriceFeedMessages
290+ );
291+ (bytes20 rootDigest , bytes [] memory proofs ) = MerkleTree
292+ .constructProofs (encodedTwapPriceFeedMessages, depth);
293+ bytes
294+ memory wormholeMerkleVaa = generateForwardCompatibleWormholeMerkleVaa (
295+ rootDigest,
296+ trailingHeaderData,
297+ numSigners
298+ );
299+
300+ whMerkleUpdateData = abi.encodePacked (
301+ generateForwardCompatibleWormholeMerkleUpdateHeader (
302+ minorVersion,
303+ trailingHeaderData
304+ ),
305+ uint16 (wormholeMerkleVaa.length ),
306+ wormholeMerkleVaa,
307+ uint8 (twapPriceFeedMessages.length )
308+ );
309+
310+ for (uint i = 0 ; i < twapPriceFeedMessages.length ; i++ ) {
311+ whMerkleUpdateData = abi.encodePacked (
312+ whMerkleUpdateData,
313+ uint16 (encodedTwapPriceFeedMessages[i].length ),
314+ encodedTwapPriceFeedMessages[i],
315+ proofs[i]
316+ );
317+ }
318+ }
319+
222320 function generateForwardCompatibleWormholeMerkleUpdateHeader (
223321 uint8 minorVersion ,
224322 bytes memory trailingHeaderData
@@ -275,6 +373,39 @@ abstract contract PythTestUtils is Test, WormholeTestUtils, RandTestUtils {
275373 priceFeedMessages[i].emaConf = prices[i].conf;
276374 }
277375 }
376+
377+ function pricesToTwapPriceFeedMessages (
378+ bytes32 [] memory priceIds ,
379+ PythStructs.Price[] memory prices ,
380+ uint64 [] memory startTimes ,
381+ uint64 [] memory endTimes ,
382+ uint64 [] memory downSlotsRatios
383+ ) public returns (TwapPriceFeedMessage[] memory twapPriceFeedMessages ) {
384+ assertGe (priceIds.length , prices.length );
385+ assertEq (prices.length , startTimes.length );
386+ assertEq (prices.length , endTimes.length );
387+ assertEq (prices.length , downSlotsRatios.length );
388+
389+ twapPriceFeedMessages = new TwapPriceFeedMessage [](prices.length );
390+
391+ for (uint i = 0 ; i < prices.length ; ++ i) {
392+ twapPriceFeedMessages[i].priceId = priceIds[i];
393+ twapPriceFeedMessages[i].startTime = startTimes[i];
394+ twapPriceFeedMessages[i].endTime = endTimes[i];
395+ twapPriceFeedMessages[i].price = prices[i].price;
396+ twapPriceFeedMessages[i].conf = prices[i].conf;
397+ twapPriceFeedMessages[i].expo = prices[i].expo;
398+ twapPriceFeedMessages[i].publishTime = uint64 (
399+ prices[i].publishTime
400+ );
401+ twapPriceFeedMessages[i].prevPublishTime =
402+ uint64 (prices[i].publishTime) -
403+ 1 ;
404+ twapPriceFeedMessages[i].emaPrice = prices[i].price;
405+ twapPriceFeedMessages[i].emaConf = prices[i].conf;
406+ twapPriceFeedMessages[i].downSlotsRatio = downSlotsRatios[i];
407+ }
408+ }
278409}
279410
280411contract PythUtilsTest is Test , WormholeTestUtils , PythTestUtils , IPythEvents {
0 commit comments