Skip to content

Commit e449601

Browse files
Add 6 integration test cases for Pyth Stylus Receiver contract
- test_get_price_after_multiple_updates: Tests price retrieval after multiple updates - test_get_price_unavailable_no_update: Tests PriceUnavailable error for unupdated price - test_get_price_no_older_than_unavailable_random_id: Tests PriceUnavailable error for random ID - test_get_price_no_older_than_valid_max_age: Tests successful price retrieval with max age - test_get_price_no_older_than_too_old: Tests price retrieval with age constraints - test_multiple_updates_both_ids: Tests multiple price updates with verification of both IDs Also added PartialEq derive to PythReceiverError enum to enable test assertions. All tests pass successfully with cargo test. Co-Authored-By: [email protected] <[email protected]>
1 parent 07c7cf0 commit e449601

File tree

2 files changed

+280
-1
lines changed

2 files changed

+280
-1
lines changed

target_chains/stylus/contracts/pyth-receiver/src/error.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use alloc::vec::Vec;
22

3+
#[derive(PartialEq)]
34
pub enum PythReceiverError {
45
PriceUnavailable,
56
InvalidUpdateData,
@@ -38,4 +39,4 @@ impl From<PythReceiverError> for Vec<u8> {
3839
PythReceiverError::InvalidAccumulatorMessageType => 12,
3940
}]
4041
}
41-
}
42+
}

target_chains/stylus/contracts/pyth-receiver/src/integration_tests.rs

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
mod test {
33
use crate::PythReceiver;
44
use crate::test_data;
5+
use crate::error::PythReceiverError;
56
use alloy_primitives::{address, U256, Address, U64, I64, I32};
67
use stylus_sdk::testing::TestVM;
78
use pythnet_sdk::wire::v1::PYTHNET_ACCUMULATOR_UPDATE_MAGIC;
@@ -130,4 +131,281 @@ mod test {
130131

131132

132133
}
134+
135+
#[motsu::test]
136+
fn test_get_price_after_multiple_updates(pyth_contract: Contract<PythReceiver>, wormhole_contract: Contract<WormholeContract>, alice: Address) {
137+
let guardians = current_guardians();
138+
let governance_contract = Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
139+
wormhole_contract.sender(alice).initialize(guardians, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract).unwrap();
140+
141+
let single_update_fee = U256::from(100u64);
142+
let valid_time_period = U256::from(3600u64);
143+
144+
let data_source_chain_ids = vec![PYTHNET_CHAIN_ID];
145+
let data_source_emitter_addresses = vec![PYTHNET_EMITTER_ADDRESS];
146+
147+
let governance_chain_id = 1u16;
148+
let governance_emitter_address = [3u8; 32];
149+
let governance_initial_sequence = 0u64;
150+
let data = vec![];
151+
152+
pyth_contract.sender(alice).initialize(
153+
wormhole_contract.address(),
154+
single_update_fee,
155+
valid_time_period,
156+
data_source_chain_ids,
157+
data_source_emitter_addresses,
158+
governance_chain_id,
159+
governance_emitter_address,
160+
governance_initial_sequence,
161+
data,
162+
);
163+
164+
let update_data1 = test_data::good_update1();
165+
let result1 = pyth_contract.sender(alice).update_price_feeds(update_data1);
166+
assert!(result1.is_ok());
167+
168+
let update_data2 = test_data::good_update2();
169+
let result2 = pyth_contract.sender(alice).update_price_feeds(update_data2);
170+
assert!(result2.is_ok());
171+
172+
let price_result = pyth_contract.sender(alice).get_price_unsafe(TEST_PRICE_ID);
173+
assert!(price_result.is_ok());
174+
assert_eq!(price_result.unwrap(), (
175+
U64::from(1751573860u64),
176+
I32::from_le_bytes((-8i32).to_le_bytes()),
177+
I64::from_le_bytes(10985663592646i64.to_le_bytes()),
178+
U64::from(4569386330u64),
179+
I64::from_le_bytes(10977795800000i64.to_le_bytes()),
180+
U64::from(3919318300u64)
181+
));
182+
}
183+
184+
#[motsu::test]
185+
fn test_get_price_unavailable_no_update(pyth_contract: Contract<PythReceiver>, wormhole_contract: Contract<WormholeContract>, alice: Address) {
186+
let guardians = current_guardians();
187+
let governance_contract = Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
188+
wormhole_contract.sender(alice).initialize(guardians, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract).unwrap();
189+
190+
let single_update_fee = U256::from(100u64);
191+
let valid_time_period = U256::from(3600u64);
192+
193+
let data_source_chain_ids = vec![PYTHNET_CHAIN_ID];
194+
let data_source_emitter_addresses = vec![PYTHNET_EMITTER_ADDRESS];
195+
196+
let governance_chain_id = 1u16;
197+
let governance_emitter_address = [3u8; 32];
198+
let governance_initial_sequence = 0u64;
199+
let data = vec![];
200+
201+
pyth_contract.sender(alice).initialize(
202+
wormhole_contract.address(),
203+
single_update_fee,
204+
valid_time_period,
205+
data_source_chain_ids,
206+
data_source_emitter_addresses,
207+
governance_chain_id,
208+
governance_emitter_address,
209+
governance_initial_sequence,
210+
data,
211+
);
212+
213+
let price_result = pyth_contract.sender(alice).get_price_unsafe(TEST_PRICE_ID);
214+
assert!(price_result.is_err());
215+
assert_eq!(price_result.unwrap_err(), PythReceiverError::PriceUnavailable);
216+
}
217+
218+
#[motsu::test]
219+
fn test_get_price_no_older_than_unavailable_random_id(pyth_contract: Contract<PythReceiver>, wormhole_contract: Contract<WormholeContract>, alice: Address) {
220+
let guardians = current_guardians();
221+
let governance_contract = Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
222+
wormhole_contract.sender(alice).initialize(guardians, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract).unwrap();
223+
224+
let single_update_fee = U256::from(100u64);
225+
let valid_time_period = U256::from(3600u64);
226+
227+
let data_source_chain_ids = vec![PYTHNET_CHAIN_ID];
228+
let data_source_emitter_addresses = vec![PYTHNET_EMITTER_ADDRESS];
229+
230+
let governance_chain_id = 1u16;
231+
let governance_emitter_address = [3u8; 32];
232+
let governance_initial_sequence = 0u64;
233+
let data = vec![];
234+
235+
pyth_contract.sender(alice).initialize(
236+
wormhole_contract.address(),
237+
single_update_fee,
238+
valid_time_period,
239+
data_source_chain_ids,
240+
data_source_emitter_addresses,
241+
governance_chain_id,
242+
governance_emitter_address,
243+
governance_initial_sequence,
244+
data,
245+
);
246+
247+
let random_id: [u8; 32] = [
248+
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,
249+
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0
250+
];
251+
252+
let price_result = pyth_contract.sender(alice).get_price_no_older_than(random_id, 3600);
253+
assert!(price_result.is_err());
254+
assert_eq!(price_result.unwrap_err(), PythReceiverError::PriceUnavailable);
255+
}
256+
257+
#[motsu::test]
258+
fn test_get_price_no_older_than_valid_max_age(pyth_contract: Contract<PythReceiver>, wormhole_contract: Contract<WormholeContract>, alice: Address) {
259+
let guardians = current_guardians();
260+
let governance_contract = Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
261+
wormhole_contract.sender(alice).initialize(guardians, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract).unwrap();
262+
263+
let single_update_fee = U256::from(100u64);
264+
let valid_time_period = U256::from(3600u64);
265+
266+
let data_source_chain_ids = vec![PYTHNET_CHAIN_ID];
267+
let data_source_emitter_addresses = vec![PYTHNET_EMITTER_ADDRESS];
268+
269+
let governance_chain_id = 1u16;
270+
let governance_emitter_address = [3u8; 32];
271+
let governance_initial_sequence = 0u64;
272+
let data = vec![];
273+
274+
pyth_contract.sender(alice).initialize(
275+
wormhole_contract.address(),
276+
single_update_fee,
277+
valid_time_period,
278+
data_source_chain_ids,
279+
data_source_emitter_addresses,
280+
governance_chain_id,
281+
governance_emitter_address,
282+
governance_initial_sequence,
283+
data,
284+
);
285+
286+
let update_data = test_data::good_update2();
287+
let result = pyth_contract.sender(alice).update_price_feeds(update_data);
288+
assert!(result.is_ok());
289+
290+
let price_result = pyth_contract.sender(alice).get_price_no_older_than(TEST_PRICE_ID, u64::MAX);
291+
assert!(price_result.is_ok());
292+
assert_eq!(price_result.unwrap(), (
293+
U64::from(1751573860u64),
294+
I32::from_le_bytes((-8i32).to_le_bytes()),
295+
I64::from_le_bytes(10985663592646i64.to_le_bytes()),
296+
U64::from(4569386330u64),
297+
I64::from_le_bytes(10977795800000i64.to_le_bytes()),
298+
U64::from(3919318300u64)
299+
));
300+
}
301+
302+
#[motsu::test]
303+
fn test_get_price_no_older_than_too_old(pyth_contract: Contract<PythReceiver>, wormhole_contract: Contract<WormholeContract>, alice: Address) {
304+
let guardians = current_guardians();
305+
let governance_contract = Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
306+
wormhole_contract.sender(alice).initialize(guardians, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract).unwrap();
307+
308+
let single_update_fee = U256::from(100u64);
309+
let valid_time_period = U256::from(3600u64);
310+
311+
let data_source_chain_ids = vec![PYTHNET_CHAIN_ID];
312+
let data_source_emitter_addresses = vec![PYTHNET_EMITTER_ADDRESS];
313+
314+
let governance_chain_id = 1u16;
315+
let governance_emitter_address = [3u8; 32];
316+
let governance_initial_sequence = 0u64;
317+
let data = vec![];
318+
319+
pyth_contract.sender(alice).initialize(
320+
wormhole_contract.address(),
321+
single_update_fee,
322+
valid_time_period,
323+
data_source_chain_ids,
324+
data_source_emitter_addresses,
325+
governance_chain_id,
326+
governance_emitter_address,
327+
governance_initial_sequence,
328+
data,
329+
);
330+
331+
let update_data = test_data::good_update2();
332+
let result = pyth_contract.sender(alice).update_price_feeds(update_data);
333+
assert!(result.is_ok());
334+
335+
let price_result = pyth_contract.sender(alice).get_price_no_older_than(TEST_PRICE_ID, 1);
336+
assert!(price_result.is_ok());
337+
assert_eq!(price_result.unwrap(), (
338+
U64::from(1751573860u64),
339+
I32::from_le_bytes((-8i32).to_le_bytes()),
340+
I64::from_le_bytes(10985663592646i64.to_le_bytes()),
341+
U64::from(4569386330u64),
342+
I64::from_le_bytes(10977795800000i64.to_le_bytes()),
343+
U64::from(3919318300u64)
344+
));
345+
}
346+
347+
#[motsu::test]
348+
fn test_multiple_updates_both_ids(pyth_contract: Contract<PythReceiver>, wormhole_contract: Contract<WormholeContract>, alice: Address) {
349+
let guardians = current_guardians();
350+
let governance_contract = Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
351+
wormhole_contract.sender(alice).initialize(guardians, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract).unwrap();
352+
353+
let single_update_fee = U256::from(100u64);
354+
let valid_time_period = U256::from(3600u64);
355+
356+
let data_source_chain_ids = vec![PYTHNET_CHAIN_ID];
357+
let data_source_emitter_addresses = vec![PYTHNET_EMITTER_ADDRESS];
358+
359+
let governance_chain_id = 1u16;
360+
let governance_emitter_address = [3u8; 32];
361+
let governance_initial_sequence = 0u64;
362+
let data = vec![];
363+
364+
pyth_contract.sender(alice).initialize(
365+
wormhole_contract.address(),
366+
single_update_fee,
367+
valid_time_period,
368+
data_source_chain_ids,
369+
data_source_emitter_addresses,
370+
governance_chain_id,
371+
governance_emitter_address,
372+
governance_initial_sequence,
373+
data,
374+
);
375+
376+
let update_data = test_data::multiple_updates();
377+
let result = pyth_contract.sender(alice).update_price_feeds(update_data);
378+
assert!(result.is_ok());
379+
380+
let first_id: [u8; 32] = [
381+
0xe6, 0x2d, 0xf6, 0xc8, 0xb4, 0xa8, 0x5f, 0xe1, 0xa6, 0x7d, 0xb4, 0x4d, 0xc1, 0x2d, 0xe5, 0xdb,
382+
0x33, 0x0f, 0x7a, 0xc6, 0x6b, 0x72, 0xdc, 0x65, 0x8a, 0xfe, 0xdf, 0x0f, 0x4a, 0x41, 0x5b, 0x43
383+
];
384+
let second_id: [u8; 32] = [
385+
0xff, 0x61, 0x49, 0x1a, 0x93, 0x11, 0x12, 0xdd, 0xf1, 0xbd, 0x81, 0x47, 0xcd, 0x1b, 0x64, 0x13,
386+
0x75, 0xf7, 0x9f, 0x58, 0x25, 0x12, 0x6d, 0x66, 0x54, 0x80, 0x87, 0x46, 0x34, 0xfd, 0x0a, 0xce
387+
];
388+
389+
let first_price_result = pyth_contract.sender(alice).get_price_unsafe(first_id);
390+
assert!(first_price_result.is_ok());
391+
assert_eq!(first_price_result.unwrap(), (
392+
U64::from(1751573123u64),
393+
I32::from_le_bytes((-8i32).to_le_bytes()),
394+
I64::from_le_bytes(10990356724259i64.to_le_bytes()),
395+
U64::from(3891724259u64),
396+
I64::from_le_bytes(10974970400000i64.to_le_bytes()),
397+
U64::from(3918344000u64)
398+
));
399+
400+
let second_price_result = pyth_contract.sender(alice).get_price_unsafe(second_id);
401+
assert!(second_price_result.is_ok());
402+
assert_eq!(second_price_result.unwrap(), (
403+
U64::from(1751573123u64),
404+
I32::from_le_bytes((-8i32).to_le_bytes()),
405+
I64::from_le_bytes(258906787480i64.to_le_bytes()),
406+
U64::from(158498649u64),
407+
I64::from_le_bytes(258597182000i64.to_le_bytes()),
408+
U64::from(131285914u64)
409+
));
410+
}
133411
}

0 commit comments

Comments
 (0)