Skip to content

Commit 02014ab

Browse files
committed
Merge branch 'pyth-stylus-update-and-initialize' into pyth-stylus-parse-updates
2 parents b4bb65f + ba64927 commit 02014ab

File tree

6 files changed

+269
-221
lines changed

6 files changed

+269
-221
lines changed

target_chains/stylus/Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

target_chains/stylus/contracts/pyth-receiver/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ stylus-sdk = { version = "0.9.0", features = ["stylus-test"] }
2626
dotenv = "0.15.0"
2727
motsu = "0.9.0"
2828
wormhole-contract = { path = "../wormhole" }
29+
mock_instant = "0.6.0"
2930

3031
[features]
3132
default = ["mini-alloc"]

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

Lines changed: 87 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,19 @@
11
#[cfg(test)]
22
mod test {
33
use crate::error::PythReceiverError;
4-
use crate::test_data;
4+
use crate::test_data::*;
55
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;
78
use motsu::prelude::*;
89
use pythnet_sdk::wire::v1::{AccumulatorUpdateData, Proof};
10+
use std::time::Duration;
911
use wormhole_contract::WormholeContract;
1012
const TEST_PRICE_ID: [u8; 32] = [
1113
0xe6, 0x2d, 0xf6, 0xc8, 0xb4, 0xa8, 0x5f, 0xe1, 0xa6, 0x7d, 0xb4, 0x4d, 0xc1, 0x2d, 0xe5,
1214
0xdb, 0x33, 0x0f, 0x7a, 0xc6, 0x6b, 0x72, 0xdc, 0x65, 0x8a, 0xfe, 0xdf, 0x0f, 0x4a, 0x41,
1315
0x5b, 0x43,
1416
];
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;
2117

2218
const PYTHNET_CHAIN_ID: u16 = 26;
2319
const PYTHNET_EMITTER_ADDRESS: [u8; 32] = [
@@ -31,44 +27,29 @@ mod test {
3127
const GOVERNANCE_CONTRACT: U256 = U256::from_limbs([4, 0, 0, 0]);
3228

3329
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]);
3431

3532
#[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+
}
7045
}
7146
}
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
7253
}
7354

7455
#[cfg(test)]
@@ -123,29 +104,19 @@ mod test {
123104
) {
124105
pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice);
125106

126-
alice.fund(U256::from(200));
127-
128-
let update_data = test_data::good_update1();
107+
let update_data = good_update1();
129108
let update_fee = mock_get_update_fee(update_data.clone()).unwrap();
130109

110+
alice.fund(update_fee);
111+
131112
let result = pyth_contract
132113
.sender_and_value(alice, update_fee)
133114
.update_price_feeds(update_data);
134115
assert!(result.is_ok());
135116

136117
let price_result = pyth_contract.sender(alice).get_price_unsafe(TEST_PRICE_ID);
137118
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());
149120
}
150121

151122
#[motsu::test]
@@ -158,7 +129,7 @@ mod test {
158129

159130
alice.fund(U256::from(200));
160131

161-
let update_data = test_data::good_update1();
132+
let update_data = good_update1();
162133
let update_fee = mock_get_update_fee(update_data.clone()).unwrap();
163134
let small_update_fee = update_fee / U256::from(2);
164135

@@ -170,43 +141,34 @@ mod test {
170141
}
171142

172143
#[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(
174145
pyth_contract: Contract<PythReceiver>,
175146
wormhole_contract: Contract<WormholeContract>,
176147
alice: Address,
177148
) {
178149
pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice);
179150

180-
alice.fund(U256::from(200));
181-
182-
let update_data1 = test_data::good_update1();
151+
let update_data1 = good_update1();
183152
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+
184159
let result1 = pyth_contract
185160
.sender_and_value(alice, update_fee1)
186161
.update_price_feeds(update_data1);
187162
assert!(result1.is_ok());
188163

189-
let update_data2 = test_data::good_update2();
190-
let update_fee2 = mock_get_update_fee(update_data2.clone()).unwrap();
191-
192164
let result2 = pyth_contract
193165
.sender_and_value(alice, update_fee2)
194166
.update_price_feeds(update_data2);
195167
assert!(result2.is_ok());
196168

197169
let price_result = pyth_contract.sender(alice).get_price_unsafe(TEST_PRICE_ID);
198170
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());
210172
}
211173

212174
#[motsu::test]
@@ -231,6 +193,7 @@ mod test {
231193
wormhole_contract: Contract<WormholeContract>,
232194
alice: Address,
233195
) {
196+
MockClock::set_time(Duration::from_secs(1761573860));
234197
pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice);
235198

236199
let random_id: [u8; 32] = [
@@ -255,13 +218,14 @@ mod test {
255218
wormhole_contract: Contract<WormholeContract>,
256219
alice: Address,
257220
) {
221+
MockClock::set_time(Duration::from_secs(1761573860));
258222
pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice);
259223

260-
alice.fund(U256::from(200));
261-
262-
let update_data = test_data::good_update2();
224+
let update_data = good_update2();
263225
let update_fee = mock_get_update_fee(update_data.clone()).unwrap();
264226

227+
alice.fund(update_fee);
228+
265229
let result = pyth_contract
266230
.sender_and_value(alice, update_fee)
267231
.update_price_feeds(update_data);
@@ -271,17 +235,7 @@ mod test {
271235
.sender(alice)
272236
.get_price_no_older_than(TEST_PRICE_ID, u64::MAX);
273237
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());
285239
}
286240

287241
#[motsu::test]
@@ -290,13 +244,14 @@ mod test {
290244
wormhole_contract: Contract<WormholeContract>,
291245
alice: Address,
292246
) {
247+
MockClock::set_time(Duration::from_secs(1761573860));
293248
pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice);
294249

295-
alice.fund(U256::from(200));
296-
297-
let update_data = test_data::good_update2();
250+
let update_data = good_update2();
298251
let update_fee = mock_get_update_fee(update_data.clone()).unwrap();
299252

253+
alice.fund(update_fee);
254+
300255
let result = pyth_contract
301256
.sender_and_value(alice, update_fee)
302257
.update_price_feeds(update_data);
@@ -313,18 +268,18 @@ mod test {
313268
}
314269

315270
#[motsu::test]
316-
fn test_multiple_updates_different_ids_updates_both(
271+
fn test_multiple_updates_in_same_vaa_different_ids_updates_both(
317272
pyth_contract: Contract<PythReceiver>,
318273
wormhole_contract: Contract<WormholeContract>,
319274
alice: Address,
320275
) {
321276
pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice);
322277

323-
alice.fund(U256::from(200));
324-
325-
let update_data = test_data::multiple_updates();
278+
let update_data = multiple_updates_same_vaa();
326279
let update_fee = mock_get_update_fee(update_data.clone()).unwrap();
327280

281+
alice.fund(update_fee);
282+
328283
let result = pyth_contract
329284
.sender_and_value(alice, update_fee)
330285
.update_price_feeds(update_data);
@@ -341,32 +296,52 @@ mod test {
341296
0x34, 0xfd, 0x0a, 0xce,
342297
];
343298

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+
344333
let first_price_result = pyth_contract.sender(alice).get_price_unsafe(first_id);
345334
assert!(first_price_result.is_ok());
346335
assert_eq!(
347336
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]
356338
);
357339

358340
let second_price_result = pyth_contract.sender(alice).get_price_unsafe(second_id);
359341
assert!(second_price_result.is_ok());
360342
assert_eq!(
361343
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]
370345
);
371346
}
372347

0 commit comments

Comments
 (0)