diff --git a/target_chains/stylus/contracts/pyth-receiver/src/error.rs b/target_chains/stylus/contracts/pyth-receiver/src/error.rs index b195711098..226a5489bc 100644 --- a/target_chains/stylus/contracts/pyth-receiver/src/error.rs +++ b/target_chains/stylus/contracts/pyth-receiver/src/error.rs @@ -28,6 +28,7 @@ pub enum PythReceiverError { GovernanceMessageAlreadyExecuted, InvalidWormholeAddressToSet, WormholeUninitialized, + AlreadyInitialized, } impl core::fmt::Debug for PythReceiverError { @@ -71,6 +72,7 @@ impl core::fmt::Debug for PythReceiverError { PythReceiverError::WormholeUninitialized => { write!(f, "Wormhole is uninitialized, please set the Wormhole address and initialize the contract first") } + PythReceiverError::AlreadyInitialized => write!(f, "AlreadyInitialized"), } } } @@ -118,6 +120,7 @@ impl core::fmt::Display for PythReceiverError { PythReceiverError::WormholeUninitialized => { write!(f, "Wormhole is uninitialized, please set the Wormhole address and initialize the contract first") } + PythReceiverError::AlreadyInitialized => write!(f, "Contract is already initialized"), } } } @@ -151,6 +154,7 @@ impl From for Vec { PythReceiverError::GovernanceMessageAlreadyExecuted => 24, PythReceiverError::InvalidWormholeAddressToSet => 25, PythReceiverError::WormholeUninitialized => 26, + PythReceiverError::AlreadyInitialized => 27, }] } } diff --git a/target_chains/stylus/contracts/pyth-receiver/src/integration_tests.rs b/target_chains/stylus/contracts/pyth-receiver/src/integration_tests.rs index 99d8c9ca0d..2eda0b0137 100644 --- a/target_chains/stylus/contracts/pyth-receiver/src/integration_tests.rs +++ b/target_chains/stylus/contracts/pyth-receiver/src/integration_tests.rs @@ -49,25 +49,11 @@ mod test { } #[cfg(test)] - fn pyth_wormhole_init( + fn pyth_init( pyth_contract: &Contract, wormhole_contract: &Contract, alice: &Address, - ) { - let guardians = current_guardians(); - let governance_contract = - Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]); - wormhole_contract - .sender(*alice) - .initialize( - guardians, - 4, - CHAIN_ID, - GOVERNANCE_CHAIN_ID, - governance_contract, - ) - .unwrap(); - + ) -> Result<(), PythReceiverError> { let single_update_fee = SINGLE_UPDATE_FEE_IN_WEI; let valid_time_period = U256::from(3600u64); @@ -87,7 +73,33 @@ mod test { governance_chain_id, governance_emitter_address, governance_initial_sequence, - ); + )?; + + Ok(()) + } + + #[cfg(test)] + fn pyth_wormhole_init( + pyth_contract: &Contract, + wormhole_contract: &Contract, + alice: &Address, + ) -> Result<(), PythReceiverError> { + let guardians = current_guardians(); + let governance_contract = + Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]); + wormhole_contract + .sender(*alice) + .initialize( + guardians, + 4, + CHAIN_ID, + GOVERNANCE_CHAIN_ID, + governance_contract, + ) + .unwrap(); + + pyth_init(pyth_contract, wormhole_contract, alice)?; + Ok(()) } #[motsu::test] @@ -96,7 +108,7 @@ mod test { wormhole_contract: Contract, alice: Address, ) { - pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice); + let _ = pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice); let update_data = ban_usd_update(); let update_fee = mock_get_update_fee(update_data.clone()).unwrap(); @@ -121,7 +133,7 @@ mod test { wormhole_contract: Contract, alice: Address, ) { - pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice); + let _ = pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice); alice.fund(U256::from(200)); @@ -142,7 +154,7 @@ mod test { wormhole_contract: Contract, alice: Address, ) { - pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice); + let _ = pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice); let update_data1 = ban_usd_update(); let update_fee1 = mock_get_update_fee(update_data1.clone()).unwrap(); @@ -175,7 +187,7 @@ mod test { wormhole_contract: Contract, alice: Address, ) { - pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice); + let _ = pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice); let price_result = pyth_contract .sender(alice) @@ -194,7 +206,7 @@ mod test { alice: Address, ) { MockClock::set_time(Duration::from_secs(1761573860)); // less than good_update2().timestamp + 1s - pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice); + let _ = pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice); let random_id: [u8; 32] = [ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, @@ -219,7 +231,7 @@ mod test { alice: Address, ) { MockClock::set_time(Duration::from_secs(1761573860)); - pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice); + let _ = pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice); let update_data = btc_usd_update(); let update_fee = mock_get_update_fee(update_data.clone()).unwrap(); @@ -245,7 +257,7 @@ mod test { alice: Address, ) { MockClock::set_time(Duration::from_secs(1761573860)); - pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice); + let _ = pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice); let update_data = btc_usd_update(); let update_fee = mock_get_update_fee(update_data.clone()).unwrap(); @@ -274,7 +286,7 @@ mod test { wormhole_contract: Contract, alice: Address, ) { - pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice); + let _ = pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice); let update_data = multiple_updates_diff_vaa(); let update_fee = mock_get_update_fee(update_data.clone()).unwrap(); @@ -311,7 +323,7 @@ mod test { wormhole_contract: Contract, alice: Address, ) { - pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice); + let _ = pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice); assert!(!pyth_contract .sender(alice) @@ -338,7 +350,7 @@ mod test { wormhole_contract: Contract, alice: Address, ) { - pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice); + let _ = pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice); let price_result = pyth_contract .sender(alice) @@ -357,7 +369,7 @@ mod test { wormhole_contract: Contract, alice: Address, ) { - pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice); + let _ = pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice); let update_data = ban_usd_update(); let update_fee = mock_get_update_fee(update_data.clone()).unwrap(); @@ -384,7 +396,7 @@ mod test { wormhole_contract: Contract, alice: Address, ) { - pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice); + let _ = pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice); let update_data = multiple_updates_diff_vaa(); let update_fee = mock_get_update_fee(update_data.clone()).unwrap(); @@ -417,4 +429,21 @@ mod test { multiple_updates_diff_vaa_results_full()[1] ); } + + #[motsu::test] + fn test_double_initialization_reverts( + pyth_contract: Contract, + wormhole_contract: Contract, + alice: Address, + ) { + let _ = pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice); + + let double_init = pyth_init(&pyth_contract, &wormhole_contract, &alice); + + assert!(double_init.is_err()); + assert_eq!( + double_init.unwrap_err(), + PythReceiverError::AlreadyInitialized + ); + } } diff --git a/target_chains/stylus/contracts/pyth-receiver/src/lib.rs b/target_chains/stylus/contracts/pyth-receiver/src/lib.rs index ab88aa1428..4e5f6fc511 100644 --- a/target_chains/stylus/contracts/pyth-receiver/src/lib.rs +++ b/target_chains/stylus/contracts/pyth-receiver/src/lib.rs @@ -80,6 +80,7 @@ pub struct PythReceiver { pub governance_data_source_index: StorageUint<32, 1>, pub latest_price_info: StorageMap, PriceFeedStorage>, pub transaction_fee_in_wei: StorageU256, + pub initialized: StorageBool, } #[public] @@ -94,7 +95,10 @@ impl PythReceiver { governance_emitter_chain_id: u16, governance_emitter_address: [u8; 32], governance_initial_sequence: u64, - ) { + ) -> Result<(), PythReceiverError> { + if self.initialized.get() { + return Err(PythReceiverError::AlreadyInitialized.into()); + } self.wormhole.set(wormhole); self.single_update_fee_in_wei.set(single_update_fee_in_wei); self.valid_time_period_seconds @@ -123,6 +127,8 @@ impl PythReceiver { self.is_valid_data_source.setter(data_source_key).set(true); } + self.initialized.set(true); + Ok(()) } pub fn price_feed_exists(&self, id: [u8; 32]) -> bool { diff --git a/target_chains/stylus/contracts/pyth-receiver/src/pyth_governance_test.rs b/target_chains/stylus/contracts/pyth-receiver/src/pyth_governance_test.rs index 11ee65c8a9..cda966070d 100644 --- a/target_chains/stylus/contracts/pyth-receiver/src/pyth_governance_test.rs +++ b/target_chains/stylus/contracts/pyth-receiver/src/pyth_governance_test.rs @@ -20,29 +20,13 @@ mod test { const GOVERNANCE_CONTRACT: U256 = U256::from_limbs([4, 0, 0, 0]); const SINGLE_UPDATE_FEE_IN_WEI: U256 = U256::from_limbs([100, 0, 0, 0]); - const TRANSACTION_FEE_IN_WEI: U256 = U256::from_limbs([32, 0, 0, 0]); - - const TEST_SIGNER1: Address = Address::new([ - 0xbe, 0xFA, 0x42, 0x9d, 0x57, 0xcD, 0x18, 0xb7, 0xF8, 0xA4, 0xd9, 0x1A, 0x2d, 0xa9, 0xAB, - 0x4A, 0xF0, 0x5d, 0x0F, 0xBe, - ]); - const TEST_SIGNER2: Address = Address::new([ - 0x4b, 0xa0, 0xC2, 0xdb, 0x9A, 0x26, 0x20, 0x8b, 0x3b, 0xB1, 0xa5, 0x0B, 0x01, 0xb1, 0x69, - 0x41, 0xc1, 0x0D, 0x76, 0xdb, - ]); + const GOVERNANCE_CHAIN_ID: u16 = 1; const GOVERNANCE_EMITTER: [u8; 32] = [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, ]; - const TEST_PYTH2_WORMHOLE_CHAIN_ID: u16 = 1; - const TEST_PYTH2_WORMHOLE_EMITTER: [u8; 32] = [ - 0x71, 0xf8, 0xdc, 0xb8, 0x63, 0xd1, 0x76, 0xe2, 0xc4, 0x20, 0xad, 0x66, 0x10, 0xcf, 0x68, - 0x73, 0x59, 0x61, 0x2b, 0x6f, 0xb3, 0x92, 0xe0, 0x64, 0x2b, 0x0c, 0xa6, 0xb1, 0xf1, 0x86, - 0xaa, 0x3b, - ]; - const TARGET_CHAIN_ID: u16 = 2; #[cfg(test)] fn pyth_wormhole_init( @@ -75,7 +59,7 @@ mod test { let governance_chain_id = 1u16; let governance_initial_sequence = 0u64; - pyth_contract.sender(*alice).initialize( + let _ = pyth_contract.sender(*alice).initialize( wormhole_contract.address(), single_update_fee, valid_time_period,