1
1
#[ cfg( test) ]
2
2
mod test {
3
3
use crate :: error:: PythReceiverError ;
4
- use crate :: test_data;
4
+ use crate :: test_data:: * ;
5
5
use crate :: PythReceiver ;
6
- use alloy_primitives:: { address, Address , I32 , I64 , U256 , U64 } ;
6
+ use alloy_primitives:: { Address , U256 } ;
7
+ use mock_instant:: global:: MockClock ;
7
8
use motsu:: prelude:: * ;
8
9
use pythnet_sdk:: wire:: v1:: { AccumulatorUpdateData , Proof } ;
10
+ use std:: time:: Duration ;
9
11
use wormhole_contract:: WormholeContract ;
10
12
const TEST_PRICE_ID : [ u8 ; 32 ] = [
11
13
0xe6 , 0x2d , 0xf6 , 0xc8 , 0xb4 , 0xa8 , 0x5f , 0xe1 , 0xa6 , 0x7d , 0xb4 , 0x4d , 0xc1 , 0x2d , 0xe5 ,
12
14
0xdb , 0x33 , 0x0f , 0x7a , 0xc6 , 0x6b , 0x72 , 0xdc , 0x65 , 0x8a , 0xfe , 0xdf , 0x0f , 0x4a , 0x41 ,
13
15
0x5b , 0x43 ,
14
16
] ;
15
- const TEST_PUBLISH_TIME : u64 = 1751563000 ;
16
- const TEST_PRICE : i64 = 10967241867779 ;
17
- const TEST_CONF : u64 = 4971244966 ;
18
- const TEST_EXPO : i32 = -8 ;
19
- const TEST_EMA_PRICE : i64 = 10942391100000 ;
20
- const TEST_EMA_CONF : u64 = 4398561400 ;
21
17
22
18
const PYTHNET_CHAIN_ID : u16 = 26 ;
23
19
const PYTHNET_EMITTER_ADDRESS : [ u8 ; 32 ] = [
@@ -31,44 +27,29 @@ mod test {
31
27
const GOVERNANCE_CONTRACT : U256 = U256 :: from_limbs ( [ 4 , 0 , 0 , 0 ] ) ;
32
28
33
29
const SINGLE_UPDATE_FEE_IN_WEI : U256 = U256 :: from_limbs ( [ 100 , 0 , 0 , 0 ] ) ;
30
+ const TRANSACTION_FEE_IN_WEI : U256 = U256 :: from_limbs ( [ 32 , 0 , 0 , 0 ] ) ;
34
31
35
32
#[ cfg( test) ]
36
- fn current_guardians ( ) -> Vec < Address > {
37
- vec ! [
38
- address!( "0x5893B5A76c3f739645648885bDCcC06cd70a3Cd3" ) , // Rockaway
39
- address!( "0xfF6CB952589BDE862c25Ef4392132fb9D4A42157" ) , // Staked
40
- address!( "0x114De8460193bdf3A2fCf81f86a09765F4762fD1" ) , // Figment
41
- address!( "0x107A0086b32d7A0977926A205131d8731D39cbEB" ) , // ChainodeTech
42
- address!( "0x8C82B2fd82FaeD2711d59AF0F2499D16e726f6b2" ) , // Inotel
43
- address!( "0x11b39756C042441BE6D8650b69b54EbE715E2343" ) , // HashKey Cloud
44
- address!( "0x54Ce5B4D348fb74B958e8966e2ec3dBd4958a7cd" ) , // ChainLayer
45
- address!( "0x15e7cAF07C4e3DC8e7C469f92C8Cd88FB8005a20" ) , // xLabs
46
- address!( "0x74a3bf913953D695260D88BC1aA25A4eeE363ef0" ) , // Forbole
47
- address!( "0x000aC0076727b35FBea2dAc28fEE5cCB0fEA768e" ) , // Staking Fund
48
- address!( "0xAF45Ced136b9D9e24903464AE889F5C8a723FC14" ) , // Moonlet Wallet
49
- address!( "0xf93124b7c738843CBB89E864c862c38cddCccF95" ) , // P2P Validator
50
- address!( "0xD2CC37A4dc036a8D232b48f62cDD4731412f4890" ) , // 01node
51
- address!( "0xDA798F6896A3331F64b48c12D1D57Fd9cbe70811" ) , // MCF
52
- address!( "0x71AA1BE1D36CaFE3867910F99C09e347899C19C3" ) , // Everstake
53
- address!( "0x8192b6E7387CCd768277c17DAb1b7a5027c0b3Cf" ) , // Chorus One
54
- address!( "0x178e21ad2E77AE06711549CFBB1f9c7a9d8096e8" ) , // Syncnode
55
- address!( "0x5E1487F35515d02A92753504a8D75471b9f49EdB" ) , // Triton
56
- address!( "0x6FbEBc898F403E4773E95feB15E80C9A99c8348d" ) , // Staking Facilities
57
- ]
58
- }
59
-
60
- #[ cfg( test) ]
61
- fn mock_get_update_fee ( update_data : Vec < u8 > ) -> Result < U256 , PythReceiverError > {
62
- let update_data_array: & [ u8 ] = & update_data;
63
- let accumulator_update = AccumulatorUpdateData :: try_from_slice ( & update_data_array)
64
- . map_err ( |_| PythReceiverError :: InvalidAccumulatorMessage ) ?;
65
- match accumulator_update. proof {
66
- Proof :: WormholeMerkle { vaa : _, updates } => {
67
- let num_updates =
68
- u8:: try_from ( updates. len ( ) ) . map_err ( |_| PythReceiverError :: TooManyUpdates ) ?;
69
- Ok ( U256 :: from ( num_updates) . saturating_mul ( SINGLE_UPDATE_FEE_IN_WEI ) )
33
+ fn mock_get_update_fee ( update_data : Vec < Vec < u8 > > ) -> Result < U256 , PythReceiverError > {
34
+ let mut total_num_updates: u64 = 0 ;
35
+ for data in & update_data {
36
+ let update_data_array: & [ u8 ] = & data;
37
+ let accumulator_update = AccumulatorUpdateData :: try_from_slice ( & update_data_array)
38
+ . map_err ( |_| PythReceiverError :: InvalidAccumulatorMessage ) ?;
39
+ match accumulator_update. proof {
40
+ Proof :: WormholeMerkle { vaa : _, updates } => {
41
+ let num_updates = u64:: try_from ( updates. len ( ) )
42
+ . map_err ( |_| PythReceiverError :: TooManyUpdates ) ?;
43
+ total_num_updates += num_updates;
44
+ }
70
45
}
71
46
}
47
+ Ok ( get_total_fee ( total_num_updates) )
48
+ }
49
+
50
+ fn get_total_fee ( total_num_updates : u64 ) -> U256 {
51
+ U256 :: from ( total_num_updates) . saturating_mul ( SINGLE_UPDATE_FEE_IN_WEI )
52
+ + TRANSACTION_FEE_IN_WEI
72
53
}
73
54
74
55
#[ cfg( test) ]
@@ -123,29 +104,19 @@ mod test {
123
104
) {
124
105
pyth_wormhole_init ( & pyth_contract, & wormhole_contract, & alice) ;
125
106
126
- alice. fund ( U256 :: from ( 200 ) ) ;
127
-
128
- let update_data = test_data:: good_update1 ( ) ;
107
+ let update_data = good_update1 ( ) ;
129
108
let update_fee = mock_get_update_fee ( update_data. clone ( ) ) . unwrap ( ) ;
130
109
110
+ alice. fund ( update_fee) ;
111
+
131
112
let result = pyth_contract
132
113
. sender_and_value ( alice, update_fee)
133
114
. update_price_feeds ( update_data) ;
134
115
assert ! ( result. is_ok( ) ) ;
135
116
136
117
let price_result = pyth_contract. sender ( alice) . get_price_unsafe ( TEST_PRICE_ID ) ;
137
118
assert ! ( price_result. is_ok( ) ) ;
138
- assert_eq ! (
139
- price_result. unwrap( ) ,
140
- (
141
- U64 :: from( TEST_PUBLISH_TIME ) ,
142
- I32 :: from_le_bytes( TEST_EXPO . to_le_bytes( ) ) ,
143
- I64 :: from_le_bytes( TEST_PRICE . to_le_bytes( ) ) ,
144
- U64 :: from( TEST_CONF ) ,
145
- I64 :: from_le_bytes( TEST_EMA_PRICE . to_le_bytes( ) ) ,
146
- U64 :: from( TEST_EMA_CONF )
147
- )
148
- ) ;
119
+ assert_eq ! ( price_result. unwrap( ) , good_update1_results( ) ) ;
149
120
}
150
121
151
122
#[ motsu:: test]
@@ -158,7 +129,7 @@ mod test {
158
129
159
130
alice. fund ( U256 :: from ( 200 ) ) ;
160
131
161
- let update_data = test_data :: good_update1 ( ) ;
132
+ let update_data = good_update1 ( ) ;
162
133
let update_fee = mock_get_update_fee ( update_data. clone ( ) ) . unwrap ( ) ;
163
134
let small_update_fee = update_fee / U256 :: from ( 2 ) ;
164
135
@@ -170,43 +141,34 @@ mod test {
170
141
}
171
142
172
143
#[ motsu:: test]
173
- fn test_get_price_after_multiple_updates_returns_recent_price (
144
+ fn test_get_price_after_multiple_different_updates_returns_recent_price (
174
145
pyth_contract : Contract < PythReceiver > ,
175
146
wormhole_contract : Contract < WormholeContract > ,
176
147
alice : Address ,
177
148
) {
178
149
pyth_wormhole_init ( & pyth_contract, & wormhole_contract, & alice) ;
179
150
180
- alice. fund ( U256 :: from ( 200 ) ) ;
181
-
182
- let update_data1 = test_data:: good_update1 ( ) ;
151
+ let update_data1 = good_update1 ( ) ;
183
152
let update_fee1 = mock_get_update_fee ( update_data1. clone ( ) ) . unwrap ( ) ;
153
+
154
+ let update_data2 = good_update2 ( ) ;
155
+ let update_fee2 = mock_get_update_fee ( update_data2. clone ( ) ) . unwrap ( ) ;
156
+
157
+ alice. fund ( update_fee1 + update_fee2) ;
158
+
184
159
let result1 = pyth_contract
185
160
. sender_and_value ( alice, update_fee1)
186
161
. update_price_feeds ( update_data1) ;
187
162
assert ! ( result1. is_ok( ) ) ;
188
163
189
- let update_data2 = test_data:: good_update2 ( ) ;
190
- let update_fee2 = mock_get_update_fee ( update_data2. clone ( ) ) . unwrap ( ) ;
191
-
192
164
let result2 = pyth_contract
193
165
. sender_and_value ( alice, update_fee2)
194
166
. update_price_feeds ( update_data2) ;
195
167
assert ! ( result2. is_ok( ) ) ;
196
168
197
169
let price_result = pyth_contract. sender ( alice) . get_price_unsafe ( TEST_PRICE_ID ) ;
198
170
assert ! ( price_result. is_ok( ) ) ;
199
- assert_eq ! (
200
- price_result. unwrap( ) ,
201
- (
202
- U64 :: from( 1751573860u64 ) ,
203
- I32 :: from_le_bytes( ( -8i32 ) . to_le_bytes( ) ) ,
204
- I64 :: from_le_bytes( 10985663592646i64 . to_le_bytes( ) ) ,
205
- U64 :: from( 4569386330u64 ) ,
206
- I64 :: from_le_bytes( 10977795800000i64 . to_le_bytes( ) ) ,
207
- U64 :: from( 3919318300u64 )
208
- )
209
- ) ;
171
+ assert_eq ! ( price_result. unwrap( ) , good_update2_results( ) ) ;
210
172
}
211
173
212
174
#[ motsu:: test]
@@ -231,6 +193,7 @@ mod test {
231
193
wormhole_contract : Contract < WormholeContract > ,
232
194
alice : Address ,
233
195
) {
196
+ MockClock :: set_time ( Duration :: from_secs ( 1761573860 ) ) ;
234
197
pyth_wormhole_init ( & pyth_contract, & wormhole_contract, & alice) ;
235
198
236
199
let random_id: [ u8 ; 32 ] = [
@@ -255,13 +218,14 @@ mod test {
255
218
wormhole_contract : Contract < WormholeContract > ,
256
219
alice : Address ,
257
220
) {
221
+ MockClock :: set_time ( Duration :: from_secs ( 1761573860 ) ) ;
258
222
pyth_wormhole_init ( & pyth_contract, & wormhole_contract, & alice) ;
259
223
260
- alice. fund ( U256 :: from ( 200 ) ) ;
261
-
262
- let update_data = test_data:: good_update2 ( ) ;
224
+ let update_data = good_update2 ( ) ;
263
225
let update_fee = mock_get_update_fee ( update_data. clone ( ) ) . unwrap ( ) ;
264
226
227
+ alice. fund ( update_fee) ;
228
+
265
229
let result = pyth_contract
266
230
. sender_and_value ( alice, update_fee)
267
231
. update_price_feeds ( update_data) ;
@@ -271,17 +235,7 @@ mod test {
271
235
. sender ( alice)
272
236
. get_price_no_older_than ( TEST_PRICE_ID , u64:: MAX ) ;
273
237
assert ! ( price_result. is_ok( ) ) ;
274
- assert_eq ! (
275
- price_result. unwrap( ) ,
276
- (
277
- U64 :: from( 1751573860u64 ) ,
278
- I32 :: from_le_bytes( ( -8i32 ) . to_le_bytes( ) ) ,
279
- I64 :: from_le_bytes( 10985663592646i64 . to_le_bytes( ) ) ,
280
- U64 :: from( 4569386330u64 ) ,
281
- I64 :: from_le_bytes( 10977795800000i64 . to_le_bytes( ) ) ,
282
- U64 :: from( 3919318300u64 )
283
- )
284
- ) ;
238
+ assert_eq ! ( price_result. unwrap( ) , good_update2_results( ) ) ;
285
239
}
286
240
287
241
#[ motsu:: test]
@@ -290,13 +244,14 @@ mod test {
290
244
wormhole_contract : Contract < WormholeContract > ,
291
245
alice : Address ,
292
246
) {
247
+ MockClock :: set_time ( Duration :: from_secs ( 1761573860 ) ) ;
293
248
pyth_wormhole_init ( & pyth_contract, & wormhole_contract, & alice) ;
294
249
295
- alice. fund ( U256 :: from ( 200 ) ) ;
296
-
297
- let update_data = test_data:: good_update2 ( ) ;
250
+ let update_data = good_update2 ( ) ;
298
251
let update_fee = mock_get_update_fee ( update_data. clone ( ) ) . unwrap ( ) ;
299
252
253
+ alice. fund ( update_fee) ;
254
+
300
255
let result = pyth_contract
301
256
. sender_and_value ( alice, update_fee)
302
257
. update_price_feeds ( update_data) ;
@@ -313,18 +268,18 @@ mod test {
313
268
}
314
269
315
270
#[ motsu:: test]
316
- fn test_multiple_updates_different_ids_updates_both (
271
+ fn test_multiple_updates_in_same_vaa_different_ids_updates_both (
317
272
pyth_contract : Contract < PythReceiver > ,
318
273
wormhole_contract : Contract < WormholeContract > ,
319
274
alice : Address ,
320
275
) {
321
276
pyth_wormhole_init ( & pyth_contract, & wormhole_contract, & alice) ;
322
277
323
- alice. fund ( U256 :: from ( 200 ) ) ;
324
-
325
- let update_data = test_data:: multiple_updates ( ) ;
278
+ let update_data = multiple_updates_same_vaa ( ) ;
326
279
let update_fee = mock_get_update_fee ( update_data. clone ( ) ) . unwrap ( ) ;
327
280
281
+ alice. fund ( update_fee) ;
282
+
328
283
let result = pyth_contract
329
284
. sender_and_value ( alice, update_fee)
330
285
. update_price_feeds ( update_data) ;
@@ -341,32 +296,52 @@ mod test {
341
296
0x34 , 0xfd , 0x0a , 0xce ,
342
297
] ;
343
298
299
+ let first_price_result = pyth_contract. sender ( alice) . get_price_unsafe ( first_id) ;
300
+ assert ! ( first_price_result. is_ok( ) ) ;
301
+ assert_eq ! ( first_price_result. unwrap( ) , multiple_updates_results( ) [ 0 ] ) ;
302
+
303
+ let second_price_result = pyth_contract. sender ( alice) . get_price_unsafe ( second_id) ;
304
+ assert ! ( second_price_result. is_ok( ) ) ;
305
+ assert_eq ! ( second_price_result. unwrap( ) , multiple_updates_results( ) [ 1 ] ) ;
306
+ }
307
+
308
+ #[ motsu:: test]
309
+ fn test_multiple_updates_different_ids_updates_both (
310
+ pyth_contract : Contract < PythReceiver > ,
311
+ wormhole_contract : Contract < WormholeContract > ,
312
+ alice : Address ,
313
+ ) {
314
+ pyth_wormhole_init ( & pyth_contract, & wormhole_contract, & alice) ;
315
+
316
+ let update_data = multiple_updates_diff_vaa ( ) ;
317
+ let update_fee = mock_get_update_fee ( update_data. clone ( ) ) . unwrap ( ) ;
318
+
319
+ alice. fund ( update_fee) ;
320
+
321
+ let result = pyth_contract
322
+ . sender_and_value ( alice, update_fee)
323
+ . update_price_feeds ( update_data) ;
324
+ assert ! ( result. is_ok( ) ) ;
325
+
326
+ let first_id: [ u8 ; 32 ] = [
327
+ 0x3f , 0xa4 , 0x25 , 0x28 , 0x48 , 0xf9 , 0xf0 , 0xa1 , 0x48 , 0x0b , 0xe6 , 0x27 , 0x45 , 0xa4 ,
328
+ 0x62 , 0x9d , 0x9e , 0xb1 , 0x32 , 0x2a , 0xeb , 0xab , 0x8a , 0x79 , 0x1e , 0x34 , 0x4b , 0x3b ,
329
+ 0x9c , 0x1a , 0xdc , 0xf5 ,
330
+ ] ;
331
+ let second_id: [ u8 ; 32 ] = TEST_PRICE_ID ;
332
+
344
333
let first_price_result = pyth_contract. sender ( alice) . get_price_unsafe ( first_id) ;
345
334
assert ! ( first_price_result. is_ok( ) ) ;
346
335
assert_eq ! (
347
336
first_price_result. unwrap( ) ,
348
- (
349
- U64 :: from( 1751573123u64 ) ,
350
- I32 :: from_le_bytes( ( -8i32 ) . to_le_bytes( ) ) ,
351
- I64 :: from_le_bytes( 10990356724259i64 . to_le_bytes( ) ) ,
352
- U64 :: from( 3891724259u64 ) ,
353
- I64 :: from_le_bytes( 10974970400000i64 . to_le_bytes( ) ) ,
354
- U64 :: from( 3918344000u64 )
355
- )
337
+ multiple_updates_diff_vaa_results( ) [ 0 ]
356
338
) ;
357
339
358
340
let second_price_result = pyth_contract. sender ( alice) . get_price_unsafe ( second_id) ;
359
341
assert ! ( second_price_result. is_ok( ) ) ;
360
342
assert_eq ! (
361
343
second_price_result. unwrap( ) ,
362
- (
363
- U64 :: from( 1751573123u64 ) ,
364
- I32 :: from_le_bytes( ( -8i32 ) . to_le_bytes( ) ) ,
365
- I64 :: from_le_bytes( 258906787480i64 . to_le_bytes( ) ) ,
366
- U64 :: from( 158498649u64 ) ,
367
- I64 :: from_le_bytes( 258597182000i64 . to_le_bytes( ) ) ,
368
- U64 :: from( 131285914u64 )
369
- )
344
+ multiple_updates_diff_vaa_results( ) [ 1 ]
370
345
) ;
371
346
}
372
347
0 commit comments