Skip to content

Commit 3a559c0

Browse files
committed
fixed update and parse functions to take in a vector of vector of u8s to support multiple updates
1 parent ab0b2a3 commit 3a559c0

File tree

4 files changed

+96
-90
lines changed

4 files changed

+96
-90
lines changed

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

Lines changed: 41 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
#[cfg(test)]
22
mod test {
3-
use crate::{error::PythReceiverError};
4-
use crate::test_data::{self, good_update2_results, multiple_updates_results};
3+
use crate::error::PythReceiverError;
54
use crate::test_data::good_update1_results;
5+
use crate::test_data::{self, good_update2_results, multiple_updates_results};
66
use crate::PythReceiver;
77
use alloy_primitives::{Address, U256};
88
use motsu::prelude::*;
@@ -13,7 +13,7 @@ mod test {
1313
0xdb, 0x33, 0x0f, 0x7a, 0xc6, 0x6b, 0x72, 0xdc, 0x65, 0x8a, 0xfe, 0xdf, 0x0f, 0x4a, 0x41,
1414
0x5b, 0x43,
1515
];
16-
16+
1717
const PYTHNET_CHAIN_ID: u16 = 26;
1818
const PYTHNET_EMITTER_ADDRESS: [u8; 32] = [
1919
0xe1, 0x01, 0xfa, 0xed, 0xac, 0x58, 0x51, 0xe3, 0x2b, 0x9b, 0x23, 0xb5, 0xf9, 0x41, 0x1a,
@@ -26,19 +26,29 @@ mod test {
2626
const GOVERNANCE_CONTRACT: U256 = U256::from_limbs([4, 0, 0, 0]);
2727

2828
const SINGLE_UPDATE_FEE_IN_WEI: U256 = U256::from_limbs([100, 0, 0, 0]);
29+
const TRANSACTION_FEE_IN_WEI: U256 = U256::from_limbs([32, 0, 0, 0]);
2930

3031
#[cfg(test)]
31-
fn mock_get_update_fee(update_data: Vec<u8>) -> Result<U256, PythReceiverError> {
32-
let update_data_array: &[u8] = &update_data;
33-
let accumulator_update = AccumulatorUpdateData::try_from_slice(&update_data_array)
34-
.map_err(|_| PythReceiverError::InvalidAccumulatorMessage)?;
35-
match accumulator_update.proof {
36-
Proof::WormholeMerkle { vaa: _, updates } => {
37-
let num_updates =
38-
u8::try_from(updates.len()).map_err(|_| PythReceiverError::TooManyUpdates)?;
39-
Ok(U256::from(num_updates).saturating_mul(SINGLE_UPDATE_FEE_IN_WEI))
32+
fn mock_get_update_fee(update_data: Vec<Vec<u8>>) -> Result<U256, PythReceiverError> {
33+
let mut total_num_updates: u64 = 0;
34+
for data in &update_data {
35+
let update_data_array: &[u8] = &data;
36+
let accumulator_update = AccumulatorUpdateData::try_from_slice(&update_data_array)
37+
.map_err(|_| PythReceiverError::InvalidAccumulatorMessage)?;
38+
match accumulator_update.proof {
39+
Proof::WormholeMerkle { vaa: _, updates } => {
40+
let num_updates = u64::try_from(updates.len())
41+
.map_err(|_| PythReceiverError::TooManyUpdates)?;
42+
total_num_updates += num_updates;
43+
}
4044
}
4145
}
46+
Ok(get_total_fee(total_num_updates))
47+
}
48+
49+
fn get_total_fee(total_num_updates: u64) -> U256 {
50+
U256::from(total_num_updates).saturating_mul(SINGLE_UPDATE_FEE_IN_WEI)
51+
+ TRANSACTION_FEE_IN_WEI
4252
}
4353

4454
#[cfg(test)]
@@ -93,22 +103,19 @@ mod test {
93103
) {
94104
pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice);
95105

96-
alice.fund(U256::from(200));
97-
98106
let update_data = test_data::good_update1();
99107
let update_fee = mock_get_update_fee(update_data.clone()).unwrap();
100108

109+
alice.fund(update_fee);
110+
101111
let result = pyth_contract
102112
.sender_and_value(alice, update_fee)
103113
.update_price_feeds(update_data);
104114
assert!(result.is_ok());
105115

106116
let price_result = pyth_contract.sender(alice).get_price_unsafe(TEST_PRICE_ID);
107117
assert!(price_result.is_ok());
108-
assert_eq!(
109-
price_result.unwrap(),
110-
good_update1_results()
111-
);
118+
assert_eq!(price_result.unwrap(), good_update1_results());
112119
}
113120

114121
#[motsu::test]
@@ -140,29 +147,27 @@ mod test {
140147
) {
141148
pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice);
142149

143-
alice.fund(U256::from(200));
144-
145150
let update_data1 = test_data::good_update1();
146151
let update_fee1 = mock_get_update_fee(update_data1.clone()).unwrap();
152+
153+
let update_data2 = test_data::good_update2();
154+
let update_fee2 = mock_get_update_fee(update_data2.clone()).unwrap();
155+
156+
alice.fund(update_fee1 + update_fee2);
157+
147158
let result1 = pyth_contract
148159
.sender_and_value(alice, update_fee1)
149160
.update_price_feeds(update_data1);
150161
assert!(result1.is_ok());
151162

152-
let update_data2 = test_data::good_update2();
153-
let update_fee2 = mock_get_update_fee(update_data2.clone()).unwrap();
154-
155163
let result2 = pyth_contract
156164
.sender_and_value(alice, update_fee2)
157165
.update_price_feeds(update_data2);
158166
assert!(result2.is_ok());
159167

160168
let price_result = pyth_contract.sender(alice).get_price_unsafe(TEST_PRICE_ID);
161169
assert!(price_result.is_ok());
162-
assert_eq!(
163-
price_result.unwrap(),
164-
good_update2_results()
165-
);
170+
assert_eq!(price_result.unwrap(), good_update2_results());
166171
}
167172

168173
#[motsu::test]
@@ -213,11 +218,11 @@ mod test {
213218
) {
214219
pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice);
215220

216-
alice.fund(U256::from(200));
217-
218221
let update_data = test_data::good_update2();
219222
let update_fee = mock_get_update_fee(update_data.clone()).unwrap();
220223

224+
alice.fund(update_fee);
225+
221226
let result = pyth_contract
222227
.sender_and_value(alice, update_fee)
223228
.update_price_feeds(update_data);
@@ -227,10 +232,7 @@ mod test {
227232
.sender(alice)
228233
.get_price_no_older_than(TEST_PRICE_ID, u64::MAX);
229234
assert!(price_result.is_ok());
230-
assert_eq!(
231-
price_result.unwrap(),
232-
good_update2_results()
233-
);
235+
assert_eq!(price_result.unwrap(), good_update2_results());
234236
}
235237

236238
#[motsu::test]
@@ -241,11 +243,11 @@ mod test {
241243
) {
242244
pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice);
243245

244-
alice.fund(U256::from(200));
245-
246246
let update_data = test_data::good_update2();
247247
let update_fee = mock_get_update_fee(update_data.clone()).unwrap();
248248

249+
alice.fund(update_fee);
250+
249251
let result = pyth_contract
250252
.sender_and_value(alice, update_fee)
251253
.update_price_feeds(update_data);
@@ -269,11 +271,11 @@ mod test {
269271
) {
270272
pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice);
271273

272-
alice.fund(U256::from(200));
273-
274274
let update_data = test_data::multiple_updates();
275275
let update_fee = mock_get_update_fee(update_data.clone()).unwrap();
276276

277+
alice.fund(update_fee);
278+
277279
let result = pyth_contract
278280
.sender_and_value(alice, update_fee)
279281
.update_price_feeds(update_data);
@@ -292,16 +294,10 @@ mod test {
292294

293295
let first_price_result = pyth_contract.sender(alice).get_price_unsafe(first_id);
294296
assert!(first_price_result.is_ok());
295-
assert_eq!(
296-
first_price_result.unwrap(),
297-
multiple_updates_results()[0]
298-
);
297+
assert_eq!(first_price_result.unwrap(), multiple_updates_results()[0]);
299298

300299
let second_price_result = pyth_contract.sender(alice).get_price_unsafe(second_id);
301300
assert!(second_price_result.is_ok());
302-
assert_eq!(
303-
second_price_result.unwrap(),
304-
multiple_updates_results()[1]
305-
);
301+
assert_eq!(second_price_result.unwrap(), multiple_updates_results()[1]);
306302
}
307303
}

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

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,21 @@ impl PythReceiver {
171171
}
172172

173173
#[payable]
174-
pub fn update_price_feeds(&mut self, update_data: Vec<u8>) -> Result<(), PythReceiverError> {
175-
self.update_price_feeds_internal(update_data)?;
174+
pub fn update_price_feeds(
175+
&mut self,
176+
update_data: Vec<Vec<u8>>,
177+
) -> Result<(), PythReceiverError> {
178+
for data in &update_data {
179+
self.update_price_feeds_internal(data.clone())?;
180+
}
181+
182+
let total_fee = self.get_update_fee(update_data)?;
183+
184+
let value = self.vm().msg_value();
185+
186+
if value < total_fee {
187+
return Err(PythReceiverError::InsufficientFee);
188+
}
176189
Ok(())
177190
}
178191

@@ -234,14 +247,6 @@ impl PythReceiver {
234247

235248
let root_digest: MerkleRoot<Keccak160> = parse_wormhole_proof(vaa)?;
236249

237-
let total_fee = self.get_update_fee(update_data)?;
238-
239-
let value = self.vm().msg_value();
240-
241-
if value < total_fee {
242-
return Err(PythReceiverError::InsufficientFee);
243-
}
244-
245250
for update in updates {
246251
let message_vec = Vec::from(update.message);
247252
let proof: MerklePath<Keccak160> = update.proof;
@@ -294,17 +299,26 @@ impl PythReceiver {
294299
Ok(())
295300
}
296301

297-
fn get_update_fee(&self, update_data: Vec<u8>) -> Result<U256, PythReceiverError> {
298-
let update_data_array: &[u8] = &update_data;
299-
let accumulator_update = AccumulatorUpdateData::try_from_slice(&update_data_array)
300-
.map_err(|_| PythReceiverError::InvalidAccumulatorMessage)?;
301-
match accumulator_update.proof {
302-
Proof::WormholeMerkle { vaa: _, updates } => {
303-
let num_updates =
304-
u8::try_from(updates.len()).map_err(|_| PythReceiverError::TooManyUpdates)?;
305-
Ok(U256::from(num_updates).saturating_mul(self.single_update_fee_in_wei.get()))
302+
fn get_update_fee(&self, update_data: Vec<Vec<u8>>) -> Result<U256, PythReceiverError> {
303+
let mut total_num_updates: u64 = 0;
304+
for data in &update_data {
305+
let update_data_array: &[u8] = &data;
306+
let accumulator_update = AccumulatorUpdateData::try_from_slice(&update_data_array)
307+
.map_err(|_| PythReceiverError::InvalidAccumulatorMessage)?;
308+
match accumulator_update.proof {
309+
Proof::WormholeMerkle { vaa: _, updates } => {
310+
let num_updates = u64::try_from(updates.len())
311+
.map_err(|_| PythReceiverError::TooManyUpdates)?;
312+
total_num_updates += num_updates;
313+
}
306314
}
307315
}
316+
Ok(self.get_total_fee(total_num_updates))
317+
}
318+
319+
fn get_total_fee(&self, total_num_updates: u64) -> U256 {
320+
U256::from(total_num_updates).saturating_mul(self.single_update_fee_in_wei.get())
321+
+ self.transaction_fee_in_wei.get()
308322
}
309323

310324
pub fn get_twap_update_fee(&self, _update_data: Vec<Vec<u8>>) -> U256 {

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

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
1-
use alloc::{vec::Vec};
1+
use alloc::vec::Vec;
22
use stylus_sdk::alloy_primitives::{keccak256, FixedBytes, B256, I32, I64, U16, U256, U64};
33
use stylus_sdk::{
44
prelude::*,
55
storage::{StorageFixedBytes, StorageI32, StorageI64, StorageKey, StorageU16, StorageU64},
66
};
77

8-
fn serialize_data_source_to_bytes(
9-
chain_id: u16,
10-
emitter_address: &[u8; 32],
11-
) -> [u8; 34] {
8+
fn serialize_data_source_to_bytes(chain_id: u16, emitter_address: &[u8; 32]) -> [u8; 34] {
129
let mut result = [0u8; 34];
1310
result[0..2].copy_from_slice(&chain_id.to_be_bytes());
1411
result[2..].copy_from_slice(emitter_address);
1512
result
1613
}
1714

18-
1915
#[derive(Debug)]
2016
#[storage]
2117
pub struct DataSourceStorage {

0 commit comments

Comments
 (0)