浏览代码

Merge pull request #2877 from pyth-network/pyth-stylus-governance-impl

feat(stylus) - governance (minus upgrade contract)
Ayush Suresh 4 月之前
父节点
当前提交
5f2ab17d16

+ 104 - 2
target_chains/stylus/contracts/pyth-receiver/src/error.rs

@@ -20,11 +20,105 @@ pub enum PythReceiverError {
     PriceFeedNotFoundWithinRange,
     PriceFeedNotFoundWithinRange,
     NoFreshUpdate,
     NoFreshUpdate,
     PriceFeedNotFound,
     PriceFeedNotFound,
+    InvalidGovernanceMessage,
+    InvalidGovernanceTarget,
+    InvalidGovernanceAction,
+    InvalidGovernanceDataSource,
+    OldGovernanceMessage,
+    GovernanceMessageAlreadyExecuted,
+    InvalidWormholeAddressToSet,
+    WormholeUninitialized,
 }
 }
 
 
 impl core::fmt::Debug for PythReceiverError {
 impl core::fmt::Debug for PythReceiverError {
-    fn fmt(&self, _: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
-        Ok(())
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        match self {
+            PythReceiverError::PriceUnavailable => write!(f, "PriceUnavailable"),
+            PythReceiverError::InvalidUpdateData => write!(f, "InvalidUpdateData"),
+            PythReceiverError::VaaVerificationFailed => write!(f, "VaaVerificationFailed"),
+            PythReceiverError::InvalidVaa => write!(f, "InvalidVaa"),
+            PythReceiverError::InvalidWormholeMessage => write!(f, "InvalidWormholeMessage"),
+            PythReceiverError::InvalidMerkleProof => write!(f, "InvalidMerkleProof"),
+            PythReceiverError::InvalidAccumulatorMessage => write!(f, "InvalidAccumulatorMessage"),
+            PythReceiverError::InvalidMerkleRoot => write!(f, "InvalidMerkleRoot"),
+            PythReceiverError::InvalidMerklePath => write!(f, "InvalidMerklePath"),
+            PythReceiverError::InvalidUnknownSource => write!(f, "InvalidUnknownSource"),
+            PythReceiverError::NewPriceUnavailable => write!(f, "NewPriceUnavailable"),
+            PythReceiverError::InvalidAccumulatorMessageType => {
+                write!(f, "InvalidAccumulatorMessageType")
+            }
+            PythReceiverError::InsufficientFee => write!(f, "InsufficientFee"),
+            PythReceiverError::InvalidEmitterAddress => write!(f, "InvalidEmitterAddress"),
+            PythReceiverError::TooManyUpdates => write!(f, "TooManyUpdates"),
+            PythReceiverError::PriceFeedNotFoundWithinRange => {
+                write!(f, "PriceFeedNotFoundWithinRange")
+            }
+            PythReceiverError::NoFreshUpdate => write!(f, "NoFreshUpdate"),
+            PythReceiverError::PriceFeedNotFound => write!(f, "PriceFeedNotFound"),
+            PythReceiverError::InvalidGovernanceMessage => write!(f, "InvalidGovernanceMessage"),
+            PythReceiverError::InvalidGovernanceTarget => write!(f, "InvalidGovernanceTarget"),
+            PythReceiverError::InvalidGovernanceAction => write!(f, "InvalidGovernanceAction"),
+            PythReceiverError::InvalidGovernanceDataSource => {
+                write!(f, "InvalidGovernanceDataSource")
+            }
+            PythReceiverError::OldGovernanceMessage => write!(f, "OldGovernanceMessage"),
+            PythReceiverError::GovernanceMessageAlreadyExecuted => {
+                write!(f, "GovernanceMessageAlreadyExecuted")
+            }
+            PythReceiverError::InvalidWormholeAddressToSet => {
+                write!(f, "InvalidWormholeAddressToSet")
+            }
+            PythReceiverError::WormholeUninitialized => {
+                write!(f, "Wormhole is uninitialized, please set the Wormhole address and initialize the contract first")
+            }
+        }
+    }
+}
+
+impl core::fmt::Display for PythReceiverError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        match self {
+            PythReceiverError::PriceUnavailable => write!(f, "Price unavailable"),
+            PythReceiverError::InvalidUpdateData => write!(f, "Invalid update data"),
+            PythReceiverError::VaaVerificationFailed => write!(f, "VAA verification failed"),
+            PythReceiverError::InvalidVaa => write!(f, "Invalid VAA"),
+            PythReceiverError::InvalidWormholeMessage => write!(f, "Invalid Wormhole message"),
+            PythReceiverError::InvalidMerkleProof => write!(f, "Invalid Merkle proof"),
+            PythReceiverError::InvalidAccumulatorMessage => {
+                write!(f, "Invalid accumulator message")
+            }
+            PythReceiverError::InvalidMerkleRoot => write!(f, "Invalid Merkle root"),
+            PythReceiverError::InvalidMerklePath => write!(f, "Invalid Merkle path"),
+            PythReceiverError::InvalidUnknownSource => write!(f, "Invalid unknown source"),
+            PythReceiverError::NewPriceUnavailable => write!(f, "New price unavailable"),
+            PythReceiverError::InvalidAccumulatorMessageType => {
+                write!(f, "Invalid accumulator message type")
+            }
+            PythReceiverError::InsufficientFee => write!(f, "Insufficient fee"),
+            PythReceiverError::InvalidEmitterAddress => write!(f, "Invalid emitter address"),
+            PythReceiverError::TooManyUpdates => write!(f, "Too many updates"),
+            PythReceiverError::PriceFeedNotFoundWithinRange => {
+                write!(f, "Price feed not found within range")
+            }
+            PythReceiverError::NoFreshUpdate => write!(f, "No fresh update"),
+            PythReceiverError::PriceFeedNotFound => write!(f, "Price feed not found"),
+            PythReceiverError::InvalidGovernanceMessage => write!(f, "Invalid governance message"),
+            PythReceiverError::InvalidGovernanceTarget => write!(f, "Invalid governance target"),
+            PythReceiverError::InvalidGovernanceAction => write!(f, "Invalid governance action"),
+            PythReceiverError::InvalidGovernanceDataSource => {
+                write!(f, "Invalid governance data source")
+            }
+            PythReceiverError::OldGovernanceMessage => write!(f, "Old governance message"),
+            PythReceiverError::GovernanceMessageAlreadyExecuted => {
+                write!(f, "Governance message already executed")
+            }
+            PythReceiverError::InvalidWormholeAddressToSet => {
+                write!(f, "Invalid Wormhole address to set")
+            }
+            PythReceiverError::WormholeUninitialized => {
+                write!(f, "Wormhole is uninitialized, please set the Wormhole address and initialize the contract first")
+            }
+        }
     }
     }
 }
 }
 
 
@@ -49,6 +143,14 @@ impl From<PythReceiverError> for Vec<u8> {
             PythReceiverError::PriceFeedNotFoundWithinRange => 16,
             PythReceiverError::PriceFeedNotFoundWithinRange => 16,
             PythReceiverError::NoFreshUpdate => 17,
             PythReceiverError::NoFreshUpdate => 17,
             PythReceiverError::PriceFeedNotFound => 18,
             PythReceiverError::PriceFeedNotFound => 18,
+            PythReceiverError::InvalidGovernanceMessage => 19,
+            PythReceiverError::InvalidGovernanceTarget => 20,
+            PythReceiverError::InvalidGovernanceAction => 21,
+            PythReceiverError::InvalidGovernanceDataSource => 22,
+            PythReceiverError::OldGovernanceMessage => 23,
+            PythReceiverError::GovernanceMessageAlreadyExecuted => 24,
+            PythReceiverError::InvalidWormholeAddressToSet => 25,
+            PythReceiverError::WormholeUninitialized => 26,
         }]
         }]
     }
     }
 }
 }

+ 337 - 0
target_chains/stylus/contracts/pyth-receiver/src/governance_structs.rs

@@ -0,0 +1,337 @@
+use crate::error::PythReceiverError;
+use crate::structs::DataSource;
+use alloc::vec::Vec;
+use stylus_sdk::alloy_primitives::{Address, FixedBytes, U16};
+
+// Magic is `PTGM` encoded as a 4 byte data: Pyth Governance Message
+const MAGIC: u32 = 0x5054474d;
+const MODULE_TARGET: u8 = 1;
+
+#[derive(Clone, Debug, PartialEq)]
+pub enum GovernanceAction {
+    UpgradeContract,
+    AuthorizeGovernanceDataSourceTransfer,
+    SetDataSources,
+    SetFee,
+    SetValidPeriod,
+    RequestGovernanceDataSourceTransfer,
+    SetWormholeAddress,
+    SetTransactionFee,
+    WithdrawFee,
+}
+
+impl TryFrom<u8> for GovernanceAction {
+    type Error = PythReceiverError;
+
+    fn try_from(value: u8) -> Result<Self, Self::Error> {
+        match value {
+            0 => Ok(GovernanceAction::UpgradeContract),
+            1 => Ok(GovernanceAction::AuthorizeGovernanceDataSourceTransfer),
+            2 => Ok(GovernanceAction::SetDataSources),
+            3 => Ok(GovernanceAction::SetFee),
+            4 => Ok(GovernanceAction::SetValidPeriod),
+            5 => Ok(GovernanceAction::RequestGovernanceDataSourceTransfer),
+            6 => Ok(GovernanceAction::SetWormholeAddress),
+            8 => Ok(GovernanceAction::SetTransactionFee),
+            9 => Ok(GovernanceAction::WithdrawFee),
+            _ => Err(PythReceiverError::InvalidGovernanceAction),
+        }
+    }
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct GovernanceInstruction {
+    pub target_chain_id: u16,
+    pub payload: GovernancePayload,
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub enum GovernancePayload {
+    UpgradeContract(UpgradeContract),
+    AuthorizeGovernanceDataSourceTransfer(AuthorizeGovernanceDataSourceTransfer),
+    SetDataSources(SetDataSources),
+    SetFee(SetFee),
+    SetValidPeriod(SetValidPeriod),
+    RequestGovernanceDataSourceTransfer(RequestGovernanceDataSourceTransfer),
+    SetWormholeAddress(SetWormholeAddress),
+    SetTransactionFee(SetTransactionFee),
+    WithdrawFee(WithdrawFee),
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct SetFee {
+    pub value: u64,
+    pub expo: u64,
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct SetValidPeriod {
+    pub valid_time_period_seconds: u64,
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct SetTransactionFee {
+    pub value: u64,
+    pub expo: u64,
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct WithdrawFee {
+    pub value: u64,
+    pub expo: u64,
+    pub target_address: Address,
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct SetDataSources {
+    pub sources: Vec<DataSource>,
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct SetWormholeAddress {
+    pub address: Address,
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct RequestGovernanceDataSourceTransfer {
+    pub governance_data_source_index: u32,
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct AuthorizeGovernanceDataSourceTransfer {
+    pub claim_vaa: Vec<u8>,
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct UpgradeContract {
+    pub new_implementation: FixedBytes<32>,
+}
+
+pub fn parse_instruction(payload: Vec<u8>) -> Result<GovernanceInstruction, PythReceiverError> {
+    if payload.len() < 8 {
+        return Err(PythReceiverError::InvalidGovernanceMessage);
+    }
+
+    let mut cursor = 0;
+
+    let magic_bytes = payload
+        .get(cursor..cursor + 4)
+        .ok_or(PythReceiverError::InvalidGovernanceMessage)?;
+
+    let magic = u32::from_be_bytes(
+        magic_bytes
+            .try_into()
+            .map_err(|_| PythReceiverError::InvalidGovernanceMessage)?,
+    );
+
+    cursor += 4;
+
+    if magic != MAGIC {
+        return Err(PythReceiverError::InvalidGovernanceMessage);
+    }
+
+    let module = payload[cursor];
+    cursor += 1;
+
+    if module != MODULE_TARGET {
+        return Err(PythReceiverError::InvalidGovernanceTarget);
+    }
+
+    let action = GovernanceAction::try_from(payload[cursor])?;
+    cursor += 1;
+
+    let target_chain_id = u16::from_be_bytes([payload[cursor], payload[cursor + 1]]);
+    cursor += 2;
+
+    let governance_payload = match action {
+        GovernanceAction::UpgradeContract => {
+            if payload.len() < cursor + 32 {
+                return Err(PythReceiverError::InvalidGovernanceMessage);
+            }
+            let mut new_implementation = [0u8; 32];
+            new_implementation.copy_from_slice(&payload[cursor..cursor + 32]);
+            cursor += 32;
+            GovernancePayload::UpgradeContract(UpgradeContract {
+                new_implementation: FixedBytes::from(new_implementation),
+            })
+        }
+        GovernanceAction::AuthorizeGovernanceDataSourceTransfer => {
+            let claim_vaa = payload[cursor..].to_vec();
+            cursor = payload.len();
+            GovernancePayload::AuthorizeGovernanceDataSourceTransfer(
+                AuthorizeGovernanceDataSourceTransfer { claim_vaa },
+            )
+        }
+        GovernanceAction::RequestGovernanceDataSourceTransfer => {
+            if payload.len() < cursor + 4 {
+                return Err(PythReceiverError::InvalidGovernanceMessage);
+            }
+            let governance_data_source_bytes = payload
+                .get(cursor..cursor + 4)
+                .ok_or(PythReceiverError::InvalidGovernanceMessage)?;
+
+            let governance_data_source_index = u32::from_be_bytes(
+                governance_data_source_bytes
+                    .try_into()
+                    .map_err(|_| PythReceiverError::InvalidGovernanceMessage)?,
+            );
+
+            cursor += 4;
+            GovernancePayload::RequestGovernanceDataSourceTransfer(
+                RequestGovernanceDataSourceTransfer {
+                    governance_data_source_index,
+                },
+            )
+        }
+        GovernanceAction::SetDataSources => {
+            if payload.len() < cursor + 1 {
+                return Err(PythReceiverError::InvalidGovernanceMessage);
+            }
+            let num_sources = payload[cursor];
+            cursor += 1;
+
+            let mut sources = Vec::new();
+            for _ in 0..num_sources {
+                if payload.len() < cursor + 34 {
+                    return Err(PythReceiverError::InvalidGovernanceMessage);
+                }
+                let emitter_chain_id = u16::from_be_bytes([payload[cursor], payload[cursor + 1]]);
+                cursor += 2;
+
+                let mut emitter_address = [0u8; 32];
+                emitter_address.copy_from_slice(&payload[cursor..cursor + 32]);
+                cursor += 32;
+
+                sources.push(DataSource {
+                    chain_id: U16::from(emitter_chain_id),
+                    emitter_address: FixedBytes::from(emitter_address),
+                });
+            }
+            GovernancePayload::SetDataSources(SetDataSources { sources })
+        }
+        GovernanceAction::SetFee => {
+            if payload.len() < cursor + 16 {
+                return Err(PythReceiverError::InvalidGovernanceMessage);
+            }
+            let fee_value_bytes = payload
+                .get(cursor..cursor + 8)
+                .ok_or(PythReceiverError::InvalidGovernanceMessage)?;
+
+            let value = u64::from_be_bytes(
+                fee_value_bytes
+                    .try_into()
+                    .map_err(|_| PythReceiverError::InvalidGovernanceMessage)?,
+            );
+
+            cursor += 8;
+
+            let expo_bytes = payload
+                .get(cursor..cursor + 8)
+                .ok_or(PythReceiverError::InvalidGovernanceMessage)?;
+            let expo = u64::from_be_bytes(
+                expo_bytes
+                    .try_into()
+                    .map_err(|_| PythReceiverError::InvalidGovernanceMessage)?,
+            );
+
+            cursor += 8;
+            GovernancePayload::SetFee(SetFee { value, expo })
+        }
+        GovernanceAction::SetValidPeriod => {
+            let valid_period_bytes = payload
+                .get(cursor..cursor + 8)
+                .ok_or(PythReceiverError::InvalidGovernanceMessage)?;
+            let valid_time_period_seconds = u64::from_be_bytes(
+                valid_period_bytes
+                    .try_into()
+                    .map_err(|_| PythReceiverError::InvalidGovernanceMessage)?,
+            );
+            cursor += 8;
+            GovernancePayload::SetValidPeriod(SetValidPeriod {
+                valid_time_period_seconds,
+            })
+        }
+        GovernanceAction::SetWormholeAddress => {
+            let address_bytes: &[u8; 20] = payload
+                .get(cursor..cursor + 20)
+                .ok_or(PythReceiverError::InvalidGovernanceMessage)?
+                .try_into()
+                .map_err(|_| PythReceiverError::InvalidGovernanceMessage)?;
+            cursor += 20;
+            GovernancePayload::SetWormholeAddress(SetWormholeAddress {
+                address: Address::from(address_bytes),
+            })
+        }
+        GovernanceAction::SetTransactionFee => {
+            let fee_value_bytes = payload
+                .get(cursor..cursor + 8)
+                .ok_or(PythReceiverError::InvalidGovernanceMessage)?;
+
+            let value = u64::from_be_bytes(
+                fee_value_bytes
+                    .try_into()
+                    .map_err(|_| PythReceiverError::InvalidGovernanceMessage)?,
+            );
+
+            cursor += 8;
+
+            let expo_bytes = payload
+                .get(cursor..cursor + 8)
+                .ok_or(PythReceiverError::InvalidGovernanceMessage)?;
+            let expo = u64::from_be_bytes(
+                expo_bytes
+                    .try_into()
+                    .map_err(|_| PythReceiverError::InvalidGovernanceMessage)?,
+            );
+
+            cursor += 8;
+            GovernancePayload::SetTransactionFee(SetTransactionFee { value, expo })
+        }
+        GovernanceAction::WithdrawFee => {
+            if payload.len() < cursor + 28 {
+                return Err(PythReceiverError::InvalidGovernanceMessage);
+            }
+
+            let mut target_address_bytes = [0u8; 20];
+            target_address_bytes.copy_from_slice(&payload[cursor..cursor + 20]);
+            cursor += 20;
+
+            let fee_value_bytes = payload
+                .get(cursor..cursor + 8)
+                .ok_or(PythReceiverError::InvalidGovernanceMessage)?;
+
+            let value = u64::from_be_bytes(
+                fee_value_bytes
+                    .try_into()
+                    .map_err(|_| PythReceiverError::InvalidGovernanceMessage)?,
+            );
+
+            cursor += 8;
+
+            let expo_bytes = payload
+                .get(cursor..cursor + 8)
+                .ok_or(PythReceiverError::InvalidGovernanceMessage)?;
+            let expo = u64::from_be_bytes(
+                expo_bytes
+                    .try_into()
+                    .map_err(|_| PythReceiverError::InvalidGovernanceMessage)?,
+            );
+
+            cursor += 8;
+            GovernancePayload::WithdrawFee(WithdrawFee {
+                value,
+                expo,
+                target_address: Address::from(target_address_bytes),
+            })
+        }
+    };
+
+    if cursor != payload.len() {
+        return Err(PythReceiverError::InvalidGovernanceMessage);
+    }
+
+    Ok(GovernanceInstruction {
+        target_chain_id,
+        payload: governance_payload,
+    })
+}

+ 1 - 0
target_chains/stylus/contracts/pyth-receiver/src/integration_tests.rs

@@ -42,6 +42,7 @@ mod test {
         Ok(get_total_fee(total_num_updates))
         Ok(get_total_fee(total_num_updates))
     }
     }
 
 
+    #[cfg(test)]
     fn get_total_fee(total_num_updates: u64) -> U256 {
     fn get_total_fee(total_num_updates: u64) -> U256 {
         U256::from(total_num_updates).saturating_mul(SINGLE_UPDATE_FEE_IN_WEI)
         U256::from(total_num_updates).saturating_mul(SINGLE_UPDATE_FEE_IN_WEI)
             + TRANSACTION_FEE_IN_WEI
             + TRANSACTION_FEE_IN_WEI

+ 324 - 6
target_chains/stylus/contracts/pyth-receiver/src/lib.rs

@@ -6,8 +6,11 @@
 extern crate alloc;
 extern crate alloc;
 
 
 mod error;
 mod error;
+mod governance_structs;
 #[cfg(test)]
 #[cfg(test)]
 mod integration_tests;
 mod integration_tests;
+#[cfg(test)]
+mod pyth_governance_test;
 mod structs;
 mod structs;
 #[cfg(test)]
 #[cfg(test)]
 mod test_data;
 mod test_data;
@@ -18,6 +21,7 @@ use mock_instant::global::MockClock;
 use alloc::vec::Vec;
 use alloc::vec::Vec;
 use stylus_sdk::{
 use stylus_sdk::{
     alloy_primitives::{Address, FixedBytes, I32, I64, U16, U256, U32, U64},
     alloy_primitives::{Address, FixedBytes, I32, I64, U16, U256, U32, U64},
+    alloy_sol_types::sol,
     call::Call,
     call::Call,
     prelude::*,
     prelude::*,
     storage::{
     storage::{
@@ -27,6 +31,7 @@ use stylus_sdk::{
 };
 };
 
 
 use error::PythReceiverError;
 use error::PythReceiverError;
+use governance_structs::*;
 use pythnet_sdk::{
 use pythnet_sdk::{
     accumulators::merkle::{MerklePath, MerkleRoot},
     accumulators::merkle::{MerklePath, MerkleRoot},
     hashers::keccak256_160::Keccak160,
     hashers::keccak256_160::Keccak160,
@@ -42,13 +47,23 @@ use pythnet_sdk::{
 use structs::{DataSource, DataSourceStorage, PriceFeedReturn, PriceFeedStorage, PriceReturn};
 use structs::{DataSource, DataSourceStorage, PriceFeedReturn, PriceFeedStorage, PriceReturn};
 use wormhole_vaas::{Readable, Vaa, Writeable};
 use wormhole_vaas::{Readable, Vaa, Writeable};
 
 
+sol! {
+    event FeeSet(uint256 indexed old_fee, uint256 indexed new_fee);
+    event TransactionFeeSet(uint256 indexed old_fee, uint256 indexed new_fee);
+    event FeeWithdrawn(address indexed target_address, uint256 fee_amount);
+    event ValidPeriodSet(uint256 indexed old_valid_period, uint256 indexed new_valid_period);
+    event DataSourcesSet(bytes32[] old_data_sources, bytes32[] new_data_sources);
+    event GovernanceDataSourceSet(uint16 old_chain_id, bytes32 old_emitter_address, uint16 new_chain_id, bytes32 new_emitter_address, uint64 initial_sequence);
+}
+
 sol_interface! {
 sol_interface! {
-    interface IWormholeContract {
-        function initialize(address[] memory initial_guardians, uint16 chain_id, uint16 governance_chain_id, address governance_contract) external;
-        function getGuardianSet(uint32 index) external view returns (uint8[] memory);
-        function parseAndVerifyVm(uint8[] memory encoded_vaa) external view returns (uint8[] memory);
-        function quorum(uint32 num_guardians) external pure returns (uint32);
-    }
+    interface IWormholeContract  {
+    function initialize(address[] memory initial_guardians, uint32 initial_guardian_set_index, uint16 chain_id, uint16 governance_chain_id, address governance_contract) external;
+    function getGuardianSet(uint32 index) external view returns (uint8[] memory);
+    function parseAndVerifyVm(uint8[] memory encoded_vaa) external view returns (uint8[] memory);
+    function quorum(uint32 num_guardians) external pure returns (uint32);
+    function chainId() external view returns (uint16);
+}
 }
 }
 
 
 #[storage]
 #[storage]
@@ -516,6 +531,70 @@ impl PythReceiver {
         price_feeds
         price_feeds
     }
     }
 
 
+    pub fn execute_governance_instruction(
+        &mut self,
+        data: Vec<u8>,
+    ) -> Result<(), PythReceiverError> {
+        let wormhole: IWormholeContract = IWormholeContract::new(self.wormhole.get());
+        let config = Call::new();
+
+        wormhole
+            .parse_and_verify_vm(config, Vec::from(data.clone()))
+            .map_err(|_| PythReceiverError::InvalidWormholeMessage)?;
+
+        let vm = Vaa::read(&mut Vec::from(data.clone()).as_slice())
+            .map_err(|_| PythReceiverError::InvalidVaa)?;
+
+        verify_governance_vm(self, vm.clone())?;
+
+        let instruction = governance_structs::parse_instruction(vm.body.payload.to_vec())
+            .map_err(|_| PythReceiverError::InvalidGovernanceMessage)?;
+
+        let chain_id_config = Call::new();
+
+        let wormhole_id = wormhole
+            .chain_id(chain_id_config)
+            .map_err(|_| PythReceiverError::WormholeUninitialized)?;
+
+        if instruction.target_chain_id != 0 && instruction.target_chain_id != wormhole_id {
+            return Err(PythReceiverError::InvalidGovernanceTarget);
+        }
+
+        match instruction.payload {
+            GovernancePayload::SetFee(payload) => {
+                self.set_fee(payload.value, payload.expo);
+            }
+            GovernancePayload::SetDataSources(payload) => {
+                set_data_sources(self, payload.sources);
+            }
+            GovernancePayload::SetWormholeAddress(payload) => {
+                self.set_wormhole_address(payload.address, data.clone())?;
+            }
+            GovernancePayload::RequestGovernanceDataSourceTransfer(_) => {
+                return Err(PythReceiverError::InvalidGovernanceMessage);
+            }
+            GovernancePayload::AuthorizeGovernanceDataSourceTransfer(payload) => {
+                self.authorize_governance_transfer(payload.claim_vaa)?;
+            }
+            GovernancePayload::UpgradeContract(_payload) => {}
+            GovernancePayload::SetValidPeriod(payload) => {
+                self.set_valid_period(payload.valid_time_period_seconds);
+            }
+            GovernancePayload::SetTransactionFee(payload) => {
+                self.set_transaction_fee(payload.value, payload.expo);
+            }
+            GovernancePayload::WithdrawFee(payload) => {
+                self.withdraw_fee(payload.value, payload.expo, payload.target_address)?;
+            }
+        }
+
+        Ok(())
+    }
+
+    fn upgrade_contract(&self, _new_implementation: FixedBytes<32>) {
+        unimplemented!("Upgrade contract not yet implemented");
+    }
+
     fn is_no_older_than(&self, publish_time: U64, max_age: u64) -> bool {
     fn is_no_older_than(&self, publish_time: U64, max_age: u64) -> bool {
         self.get_current_timestamp()
         self.get_current_timestamp()
             .saturating_sub(publish_time.to::<u64>())
             .saturating_sub(publish_time.to::<u64>())
@@ -534,6 +613,209 @@ impl PythReceiver {
             self.vm().block_timestamp()
             self.vm().block_timestamp()
         }
         }
     }
     }
+
+    fn set_fee(&mut self, value: u64, expo: u64) {
+        let new_fee = U256::from(value).saturating_mul(U256::from(10).pow(U256::from(expo)));
+        let old_fee = self.single_update_fee_in_wei.get();
+
+        self.single_update_fee_in_wei.set(new_fee);
+
+        log(self.vm(), FeeSet { old_fee, new_fee });
+    }
+
+    fn set_valid_period(&mut self, valid_time_period_seconds: u64) {
+        let old_valid_period = self.valid_time_period_seconds.get();
+        let new_valid_period = U256::from(valid_time_period_seconds);
+        self.valid_time_period_seconds.set(new_valid_period);
+
+        log(
+            self.vm(),
+            ValidPeriodSet {
+                old_valid_period,
+                new_valid_period,
+            },
+        );
+    }
+
+    fn set_wormhole_address(
+        &mut self,
+        address: Address,
+        data: Vec<u8>,
+    ) -> Result<(), PythReceiverError> {
+        let wormhole: IWormholeContract = IWormholeContract::new(address);
+        let config = Call::new();
+
+        // Make sure the contract at the new Wormhole address is initialized and functional by testing it on the SetWormholeAddress instruction VAA
+        wormhole
+            .parse_and_verify_vm(config, data.clone())
+            .map_err(|_| PythReceiverError::InvalidVaa)?;
+
+        let vm = Vaa::read(&mut data.as_slice())
+            .map_err(|_| PythReceiverError::VaaVerificationFailed)?;
+
+        if vm.body.emitter_chain != self.governance_data_source_chain_id.get().to::<u16>() {
+            return Err(PythReceiverError::InvalidGovernanceMessage);
+        }
+
+        if vm.body.emitter_address.as_slice()
+            != self.governance_data_source_emitter_address.get().as_slice()
+        {
+            return Err(PythReceiverError::InvalidGovernanceMessage);
+        }
+
+        if vm.body.sequence.to::<u64>() <= self.last_executed_governance_sequence.get().to::<u64>()
+        {
+            return Err(PythReceiverError::InvalidWormholeAddressToSet);
+        }
+
+        let data = governance_structs::parse_instruction(vm.body.payload.to_vec())
+            .map_err(|_| PythReceiverError::InvalidGovernanceMessage)?;
+
+        match data.payload {
+            GovernancePayload::SetWormholeAddress(payload) => {
+                if payload.address != address {
+                    return Err(PythReceiverError::InvalidWormholeAddressToSet);
+                }
+            }
+            _ => return Err(PythReceiverError::InvalidGovernanceMessage),
+        }
+
+        self.wormhole.set(address);
+        Ok(())
+    }
+
+    fn authorize_governance_transfer(
+        &mut self,
+        claim_vaa: Vec<u8>,
+    ) -> Result<(), PythReceiverError> {
+        let wormhole: IWormholeContract = IWormholeContract::new(self.wormhole.get());
+        let config = Call::new();
+        wormhole
+            .parse_and_verify_vm(config, claim_vaa.clone())
+            .map_err(|_| PythReceiverError::InvalidWormholeMessage)?;
+
+        let claim_vm = Vaa::read(&mut Vec::from(claim_vaa).as_slice())
+            .map_err(|_| PythReceiverError::VaaVerificationFailed)?;
+
+        let instruction = governance_structs::parse_instruction(claim_vm.body.payload.to_vec())
+            .map_err(|_| PythReceiverError::InvalidGovernanceMessage)?;
+
+        let config2 = Call::new();
+        if instruction.target_chain_id != 0
+            && instruction.target_chain_id != wormhole.chain_id(config2).unwrap_or(0)
+        {
+            return Err(PythReceiverError::InvalidGovernanceTarget);
+        }
+
+        let request_payload = match instruction.payload {
+            GovernancePayload::RequestGovernanceDataSourceTransfer(payload) => payload,
+            _ => return Err(PythReceiverError::InvalidGovernanceMessage),
+        };
+
+        let current_index = self.governance_data_source_index.get().to::<u32>();
+        let new_index = request_payload.governance_data_source_index;
+
+        if current_index >= new_index {
+            return Err(PythReceiverError::OldGovernanceMessage);
+        }
+
+        self.governance_data_source_index.set(U32::from(new_index));
+        let old_data_source_emitter_address = self.governance_data_source_emitter_address.get();
+
+        self.governance_data_source_chain_id
+            .set(U16::from(claim_vm.body.emitter_chain));
+        let emitter_bytes: [u8; 32] = claim_vm
+            .body
+            .emitter_address
+            .as_slice()
+            .try_into()
+            .map_err(|_| PythReceiverError::InvalidEmitterAddress)?;
+        self.governance_data_source_emitter_address
+            .set(FixedBytes::from(emitter_bytes));
+
+        let last_executed_governance_sequence = claim_vm.body.sequence.to::<u64>();
+        self.last_executed_governance_sequence
+            .set(U64::from(last_executed_governance_sequence));
+
+        log(
+            self.vm(),
+            GovernanceDataSourceSet {
+                old_chain_id: current_index as u16,
+                old_emitter_address: old_data_source_emitter_address,
+                new_chain_id: claim_vm.body.emitter_chain,
+                new_emitter_address: FixedBytes::from(emitter_bytes),
+                initial_sequence: last_executed_governance_sequence,
+            },
+        );
+
+        Ok(())
+    }
+
+    fn set_transaction_fee(&mut self, value: u64, expo: u64) {
+        let new_fee = U256::from(value).saturating_mul(U256::from(10).pow(U256::from(expo)));
+        let old_fee = self.transaction_fee_in_wei.get();
+
+        self.transaction_fee_in_wei.set(new_fee);
+
+        log(self.vm(), TransactionFeeSet { old_fee, new_fee });
+    }
+
+    fn withdraw_fee(
+        &mut self,
+        value: u64,
+        expo: u64,
+        target_address: Address,
+    ) -> Result<(), PythReceiverError> {
+        let fee_to_withdraw =
+            U256::from(value).saturating_mul(U256::from(10).pow(U256::from(expo)));
+        let current_balance = self.vm().balance(self.vm().contract_address());
+
+        if current_balance < fee_to_withdraw {
+            return Err(PythReceiverError::InsufficientFee);
+        }
+
+        self.vm()
+            .transfer_eth(target_address, fee_to_withdraw)
+            .map_err(|_| PythReceiverError::InsufficientFee)?;
+
+        log(
+            self.vm(),
+            FeeWithdrawn {
+                target_address,
+                fee_amount: fee_to_withdraw,
+            },
+        );
+
+        Ok(())
+    }
+}
+
+fn verify_governance_vm(receiver: &mut PythReceiver, vm: Vaa) -> Result<(), PythReceiverError> {
+    if vm.body.emitter_chain != receiver.governance_data_source_chain_id.get().to::<u16>() {
+        return Err(PythReceiverError::InvalidGovernanceMessage);
+    }
+
+    if vm.body.emitter_address.as_slice()
+        != receiver
+            .governance_data_source_emitter_address
+            .get()
+            .as_slice()
+    {
+        return Err(PythReceiverError::InvalidGovernanceMessage);
+    }
+
+    let current_sequence = vm.body.sequence.to::<u64>();
+    let last_executed_sequence = receiver.last_executed_governance_sequence.get().to::<u64>();
+
+    if current_sequence <= last_executed_sequence {
+        return Err(PythReceiverError::GovernanceMessageAlreadyExecuted);
+    }
+
+    receiver
+        .last_executed_governance_sequence
+        .set(U64::from(current_sequence));
+
+    Ok(())
 }
 }
 
 
 fn parse_wormhole_proof(vaa: Vaa) -> Result<MerkleRoot<Keccak160>, PythReceiverError> {
 fn parse_wormhole_proof(vaa: Vaa) -> Result<MerkleRoot<Keccak160>, PythReceiverError> {
@@ -544,3 +826,39 @@ fn parse_wormhole_proof(vaa: Vaa) -> Result<MerkleRoot<Keccak160>, PythReceiverE
     });
     });
     Ok(root)
     Ok(root)
 }
 }
+
+fn set_data_sources(receiver: &mut PythReceiver, data_sources: Vec<DataSource>) {
+    let mut old_data_sources = Vec::new();
+    for i in 0..receiver.valid_data_sources.len() {
+        if let Some(storage_data_source) = receiver.valid_data_sources.get(i) {
+            let data_source = DataSource {
+                chain_id: storage_data_source.chain_id.get(),
+                emitter_address: storage_data_source.emitter_address.get(),
+            };
+            old_data_sources.push(data_source.emitter_address);
+            receiver.is_valid_data_source.setter(data_source).set(false);
+        }
+    }
+
+    receiver.valid_data_sources.erase();
+
+    let mut new_data_sources = Vec::new();
+    for data_source in data_sources {
+        let mut storage_data_source = receiver.valid_data_sources.grow();
+        storage_data_source.chain_id.set(data_source.chain_id);
+        storage_data_source
+            .emitter_address
+            .set(data_source.emitter_address);
+
+        new_data_sources.push(data_source.emitter_address);
+        receiver.is_valid_data_source.setter(data_source).set(true);
+    }
+
+    log(
+        receiver.vm(),
+        DataSourcesSet {
+            old_data_sources,
+            new_data_sources,
+        },
+    );
+}

+ 352 - 0
target_chains/stylus/contracts/pyth-receiver/src/pyth_governance_test.rs

@@ -0,0 +1,352 @@
+#[cfg(test)]
+mod test {
+    use crate::{DataSourcesSet, FeeSet, GovernanceDataSourceSet, PythReceiver, TransactionFeeSet};
+    use alloy_primitives::{address, Address, FixedBytes, U256};
+    use hex::FromHex;
+    use motsu::prelude::*;
+    use wormhole_contract::WormholeContract;
+
+    const PYTHNET_CHAIN_ID: u16 = 26;
+    const PYTHNET_EMITTER_ADDRESS: [u8; 32] = [
+        0xe1, 0x01, 0xfa, 0xed, 0xac, 0x58, 0x51, 0xe3, 0x2b, 0x9b, 0x23, 0xb5, 0xf9, 0x41, 0x1a,
+        0x8c, 0x2b, 0xac, 0x4a, 0xae, 0x3e, 0xd4, 0xdd, 0x7b, 0x81, 0x1d, 0xd1, 0xa7, 0x2e, 0xa4,
+        0xaa, 0x71,
+    ];
+
+    const CHAIN_ID: u16 = 2;
+    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(
+        pyth_contract: &Contract<PythReceiver>,
+        wormhole_contract: &Contract<WormholeContract>,
+        alice: &Address,
+        guardian_set_index: u32,
+    ) {
+        let guardians = vec![address!("0x7e5f4552091a69125d5dfcb7b8c2659029395bdf")];
+
+        let governance_contract =
+            Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
+        wormhole_contract
+            .sender(*alice)
+            .initialize(
+                guardians,
+                guardian_set_index,
+                CHAIN_ID,
+                GOVERNANCE_CHAIN_ID,
+                governance_contract,
+            )
+            .unwrap();
+
+        let single_update_fee = SINGLE_UPDATE_FEE_IN_WEI;
+        let valid_time_period = U256::from(3600u64);
+
+        let data_source_chain_ids = vec![PYTHNET_CHAIN_ID];
+        let data_source_emitter_addresses = vec![PYTHNET_EMITTER_ADDRESS];
+
+        let governance_chain_id = 1u16;
+        let governance_initial_sequence = 0u64;
+
+        pyth_contract.sender(*alice).initialize(
+            wormhole_contract.address(),
+            single_update_fee,
+            valid_time_period,
+            data_source_chain_ids,
+            data_source_emitter_addresses,
+            governance_chain_id,
+            GOVERNANCE_EMITTER,
+            governance_initial_sequence,
+        );
+    }
+
+    #[motsu::test]
+    fn test_set_data_sources(
+        pyth_contract: Contract<PythReceiver>,
+        wormhole_contract: Contract<WormholeContract>,
+        alice: Address,
+    ) {
+        pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice, 0);
+
+        let hex_str = "0100000000010069825ef00344cf745b6e72a41d4f869d4e90de517849360c72bf94efc97681671d826e484747b21a80c8f1e7816021df9f55e458a6e7a717cb2bd2a1e85fd57100499602d200000000000100000000000000000000000000000000000000000000000000000000000000110000000000000001005054474d010200020100010000000000000000000000000000000000000000000000000000000000001111";
+        let bytes = Vec::from_hex(hex_str).expect("Invalid hex string");
+
+        let result = pyth_contract
+            .sender(alice)
+            .execute_governance_instruction(bytes.clone());
+        assert!(result.is_ok());
+
+        let expected_event = DataSourcesSet {
+            old_data_sources: vec![FixedBytes::from(PYTHNET_EMITTER_ADDRESS)],
+            new_data_sources: vec![FixedBytes::from([
+                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, 0x11,
+            ])],
+        };
+        assert!(
+            pyth_contract.emitted(&expected_event),
+            "DataSourcesSet event should be emitted"
+        );
+
+        let result2 = pyth_contract
+            .sender(alice)
+            .execute_governance_instruction(bytes.clone());
+        assert!(
+            result2.is_err(),
+            "Second execution should fail due to sequence number check"
+        );
+    }
+
+    #[motsu::test]
+    fn test_set_valid_period(
+        pyth_contract: Contract<PythReceiver>,
+        wormhole_contract: Contract<WormholeContract>,
+        alice: Address,
+    ) {
+        pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice, 0);
+
+        let hex_str = "010000000001006fc16df905b08c16553eda9d5a7898ec7eba4267ce0af7945625c955e8f435fc7df7a4087af360f88c2477f0c2f4e7eaa4bb1e8fd43677f4d6b04ee20e225186000000000100000000000100000000000000000000000000000000000000000000000000000000000000110000000000000001005054474d010400020000000000000000";
+        let bytes = Vec::from_hex(hex_str).expect("Invalid hex string");
+
+        let result = pyth_contract
+            .sender(alice)
+            .execute_governance_instruction(bytes.clone());
+
+        println!("Result: {:?}", result.unwrap_err());
+        // assert!(result.is_ok());
+    }
+
+    #[motsu::test]
+    fn test_set_fee(
+        pyth_contract: Contract<PythReceiver>,
+        wormhole_contract: Contract<WormholeContract>,
+        alice: Address,
+    ) {
+        pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice, 0);
+
+        let hex_str = "0100000000010057940f58a6a44c93606bd721701539e0da93d5ea1583a735fbb13ecbcf9c01fc70240de519ea76869af14d067d68c5f3f2230f565f41b7009f3c3e63749353ed000000000100000000000100000000000000000000000000000000000000000000000000000000000000110000000000000001005054474d0103000200000000000000050000000000000003";
+        let bytes = Vec::from_hex(hex_str).expect("Invalid hex string");
+
+        let result = pyth_contract
+            .sender(alice)
+            .execute_governance_instruction(bytes.clone());
+
+        assert!(result.is_ok());
+
+        let expected_new_fee = U256::from(5000);
+        let expected_event = FeeSet {
+            old_fee: SINGLE_UPDATE_FEE_IN_WEI,
+            new_fee: expected_new_fee,
+        };
+        assert!(
+            pyth_contract.emitted(&expected_event),
+            "FeeSet event should be emitted"
+        );
+
+        let result2 = pyth_contract
+            .sender(alice)
+            .execute_governance_instruction(bytes.clone());
+        assert!(
+            result2.is_err(),
+            "Second execution should fail due to sequence number check"
+        );
+    }
+
+    #[motsu::test]
+    fn test_set_fee_in_token(
+        pyth_contract: Contract<PythReceiver>,
+        wormhole_contract: Contract<WormholeContract>,
+        alice: Address,
+    ) {
+        pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice, 0);
+
+        let hex_str = "0100000000010051c35e992b6dcfc81f02b430914694b4fbbeae7f952f3d3f1ff350f332d1d24916e2f56336ce0c392247e8c1fbb1b74eac87a68d681c729fa860f3788ece2788000000000100000000000100000000000000000000000000000000000000000000000000000000000000110000000000000001005054474d0107000200000000000000050000000000000003147e5f4552091a69125d5dfcb7b8c2659029395bdf";
+        let bytes = Vec::from_hex(hex_str).expect("Invalid hex string");
+
+        let result = pyth_contract
+            .sender(alice)
+            .execute_governance_instruction(bytes.clone());
+        if result.is_err() {
+            println!("Error: {:?}", result.as_ref().unwrap_err());
+        }
+        assert!(result.is_ok());
+
+        let result2 = pyth_contract
+            .sender(alice)
+            .execute_governance_instruction(bytes.clone());
+        assert!(
+            result2.is_err(),
+            "Second execution should fail due to sequence number check"
+        );
+    }
+
+    // This test is commented out because it requires an already deployed new Wormhole contract.
+    // This function demonstrates the usage of this instruction, however.
+    /*
+        #[motsu::test]
+        fn test_set_wormhole_address(
+            pyth_contract: Contract<PythReceiver>,
+            wormhole_contract: Contract<WormholeContract>,
+            wormhole_contract_2: Contract<WormholeContract>,
+            alice: Address,
+        ) {
+            pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice, 0);
+
+        let guardians = vec![address!("0x7e5f4552091a69125d5dfcb7b8c2659029395bdf")];
+        let governance_contract =
+            Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
+        wormhole_contract_2
+            .sender(alice)
+            .initialize(
+                guardians,
+                0,
+                CHAIN_ID,
+                GOVERNANCE_CHAIN_ID,
+                governance_contract,
+            )
+            .unwrap();
+
+
+
+        let hex_str = format!("010000000001001daf08e5e3799cbc6096a90c2361e43220325418f377620a7a73d6bece18322679f6ada9725d9081743805efb8bccecd51098f1d76f34cba8b835fae643bbd9c000000000100000000000100000000000000000000000000000000000000000000000000000000000000110000000000000001005054474d01060002{:040x}", wormhole_contract_2.address());
+        let bytes = Vec::from_hex(&hex_str).expect("Invalid hex string");
+
+        let result = pyth_contract
+            .sender(alice)
+            .execute_governance_instruction(bytes.clone());
+        if result.is_err() {
+            println!(
+                "SetWormholeAddress Error: {:?}",
+                result.as_ref().unwrap_err()
+            );
+        }
+    */
+
+    #[motsu::test]
+    fn test_authorize_governance_data_source_transfer(
+        pyth_contract: Contract<PythReceiver>,
+        wormhole_contract: Contract<WormholeContract>,
+        alice: Address,
+    ) {
+        pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice, 0);
+
+        let hex_str = "01000000000100b441e497034be4ee82242a866461d5e6744082654f71301a96f579f629b6bf176cc0c1964cd7d4f792436b7a73fc7024d72b138869b4d81d449740bb08148238000000000100000000000100000000000000000000000000000000000000000000000000000000000000110000000000000001005054474d01010002010000000001009c9dc62e92fefe0806dce30b662a5d319417a62dccc700b5f2678306d39c005f7a5e74d11df287301d85d328a3d000c5d793c57161f3150c7eb1a17668946e6b010000000100000000000100000000000000000000000000000000000000000000000000000000000000110000000000000064005054474d0105000200000000";
+        let bytes = Vec::from_hex(hex_str).expect("Invalid hex string");
+
+        let result = pyth_contract
+            .sender(alice)
+            .execute_governance_instruction(bytes.clone());
+        if result.is_err() {
+            println!(
+                "AuthorizeGovernanceDataSourceTransfer Error: {:?}",
+                result.as_ref().unwrap_err()
+            );
+        }
+        assert!(result.is_ok());
+
+        let expected_event = GovernanceDataSourceSet {
+            old_chain_id: 0, // Initial governance_data_source_index
+            old_emitter_address: FixedBytes::from(GOVERNANCE_EMITTER), // Initial governance emitter from pyth_wormhole_init
+            new_chain_id: 1, // claim_vm.body.emitter_chain from the VAA
+            new_emitter_address: FixedBytes::from(GOVERNANCE_EMITTER), // emitter_bytes from the VAA
+            initial_sequence: 100, // claim_vm.body.sequence from the VAA (0x64 = 100)
+        };
+        assert!(
+            pyth_contract.emitted(&expected_event),
+            "GovernanceDataSourceSet event should be emitted"
+        );
+    }
+
+    #[motsu::test]
+    fn test_set_transaction_fee(
+        pyth_contract: Contract<PythReceiver>,
+        wormhole_contract: Contract<WormholeContract>,
+        alice: Address,
+    ) {
+        pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice, 0);
+
+        let hex_str = "010000000001001554008232e74cb3ac74acc4527ead8a39637c537ec9b3d1fbb624c1f4f52e341e24ae89d978e033f5345e4af244df0ec61f380d9e33330f439d2b6764850270010000000100000000000100000000000000000000000000000000000000000000000000000000000000110000000000000001005054474d0108000200000000000000640000000000000003";
+        let bytes = Vec::from_hex(hex_str).expect("Invalid hex string");
+
+        let result = pyth_contract
+            .sender(alice)
+            .execute_governance_instruction(bytes.clone());
+        if result.is_err() {
+            println!(
+                "SetTransactionFee Error: {:?}",
+                result.as_ref().unwrap_err()
+            );
+        }
+        assert!(result.is_ok());
+
+        let expected_new_fee = U256::from(100000);
+        let expected_event = TransactionFeeSet {
+            old_fee: U256::ZERO,
+            new_fee: expected_new_fee,
+        };
+        assert!(
+            pyth_contract.emitted(&expected_event),
+            "TransactionFeeSet event should be emitted"
+        );
+
+        let result2 = pyth_contract
+            .sender(alice)
+            .execute_governance_instruction(bytes.clone());
+        assert!(
+            result2.is_err(),
+            "Second execution should fail due to sequence number check"
+        );
+    }
+
+    // Fee transfers can't be done in the motsu testing framework. This commented test serves as an example for how to use the function, though.
+
+    /*
+    #[motsu::test]
+    fn test_withdraw_fee(
+        pyth_contract: Contract<PythReceiver>,
+        wormhole_contract: Contract<WormholeContract>,
+        alice: Address,
+    ) {
+        pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice, 0);
+
+        let hex_str = "0100000000010030f48904e130d76ee219bc59988f89526e5c9860e89efda3a74e33c3ab53d4e6036d1c67249d2f25a27e8c94d203609785839e3e4817d0a03214ea8bbf6a8415000000000100000000000100000000000000000000000000000000000000000000000000000000000000110000000000000001005054474d0109000270997970c51812dc3a010c7d01b50e0d17dc79c800000000000000640000000000000003";
+        let bytes = Vec::from_hex(&hex_str).expect("Invalid hex string");
+
+        pyth_contract.address().fund(U256::from(200000u64));
+
+        let result = pyth_contract
+            .sender(alice)
+            .execute_governance_instruction(bytes.clone());
+
+        if result.is_err() {
+            println!("WithdrawFee Error: {:?}", result.as_ref().unwrap_err());
+        }
+        assert!(result.is_ok());
+    }
+    */
+}

+ 7 - 0
target_chains/stylus/contracts/pyth-receiver/src/structs.rs

@@ -19,6 +19,13 @@ pub struct DataSourceStorage {
     pub emitter_address: StorageFixedBytes<32>,
     pub emitter_address: StorageFixedBytes<32>,
 }
 }
 
 
+impl Erase for DataSourceStorage {
+    fn erase(&mut self) {
+        self.chain_id.erase();
+        self.emitter_address.erase();
+    }
+}
+
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct DataSource {
 pub struct DataSource {
     pub chain_id: U16,
     pub chain_id: U16,

+ 163 - 104
target_chains/stylus/contracts/wormhole/src/lib.rs

@@ -1,6 +1,5 @@
 #![cfg_attr(not(any(test, feature = "export-abi")), no_main)]
 #![cfg_attr(not(any(test, feature = "export-abi")), no_main)]
 #![cfg_attr(not(any(test, feature = "export-abi")), no_std)]
 #![cfg_attr(not(any(test, feature = "export-abi")), no_std)]
-
 #![macro_use]
 #![macro_use]
 extern crate alloc;
 extern crate alloc;
 
 
@@ -9,9 +8,9 @@ static ALLOC: mini_alloc::MiniAlloc = mini_alloc::MiniAlloc::INIT;
 
 
 use alloc::{vec, vec::Vec};
 use alloc::{vec, vec::Vec};
 use stylus_sdk::{
 use stylus_sdk::{
-    prelude::*,
-    storage::{StorageMap, StorageUint, StorageAddress, StorageBool},
     alloy_primitives::{Address, FixedBytes, U256, keccak256},
     alloy_primitives::{Address, FixedBytes, U256, keccak256},
+    prelude::*,
+    storage::{StorageAddress, StorageBool, StorageMap, StorageUint},
 };
 };
 
 
 use k256::ecdsa::{RecoveryId, Signature, VerifyingKey};
 use k256::ecdsa::{RecoveryId, Signature, VerifyingKey};
@@ -125,9 +124,11 @@ impl WormholeContract {
         if self.initialized.get() {
         if self.initialized.get() {
             return Err(WormholeError::AlreadyInitialized.into());
             return Err(WormholeError::AlreadyInitialized.into());
         }
         }
-        self.current_guardian_set_index.set(U256::from(initial_guardian_set_index));
+        self.current_guardian_set_index
+            .set(U256::from(initial_guardian_set_index));
         self.chain_id.set(U256::from(chain_id));
         self.chain_id.set(U256::from(chain_id));
-        self.governance_chain_id.set(U256::from(governance_chain_id));
+        self.governance_chain_id
+            .set(U256::from(governance_chain_id));
         self.governance_contract.set(governance_contract);
         self.governance_contract.set(governance_contract);
 
 
         self.store_gs(initial_guardian_set_index, initial_guardians, 0)?;
         self.store_gs(initial_guardian_set_index, initial_guardians, 0)?;
@@ -148,7 +149,7 @@ impl WormholeContract {
                     encoded.extend_from_slice(address.as_slice());
                     encoded.extend_from_slice(address.as_slice());
                 }
                 }
                 Ok(encoded)
                 Ok(encoded)
-            },
+            }
             Err(e) => Err(e.into()),
             Err(e) => Err(e.into()),
         }
         }
     }
     }
@@ -172,6 +173,9 @@ impl WormholeContract {
     fn quorum(num_guardians: u32) -> u32 {
     fn quorum(num_guardians: u32) -> u32 {
         (num_guardians * 2) / 3 + 1
         (num_guardians * 2) / 3 + 1
     }
     }
+    fn chain_id(&self) -> u16 {
+        self.chain_id.get().try_into().unwrap_or(0u16)
+    }
 }
 }
 
 
 impl WormholeContract {
 impl WormholeContract {
@@ -185,118 +189,131 @@ impl WormholeContract {
         if encoded_vaa.len() < 6 {
         if encoded_vaa.len() < 6 {
             return Err(WormholeError::InvalidVAAFormat);
             return Err(WormholeError::InvalidVAAFormat);
         }
         }
-    
+
         let mut cursor = 0;
         let mut cursor = 0;
-    
+
         // Get version
         // Get version
-        let version = encoded_vaa.get(cursor)
+        let version = encoded_vaa
+            .get(cursor)
             .ok_or(WormholeError::InvalidVAAFormat)?;
             .ok_or(WormholeError::InvalidVAAFormat)?;
         cursor += 1;
         cursor += 1;
-    
+
         if *version != 1 {
         if *version != 1 {
             return Err(WormholeError::InvalidVAAFormat);
             return Err(WormholeError::InvalidVAAFormat);
         }
         }
-    
+
         // Get guardian set index
         // Get guardian set index
-        let gsi_bytes = encoded_vaa.get(cursor..cursor + 4)
+        let gsi_bytes = encoded_vaa
+            .get(cursor..cursor + 4)
             .ok_or(WormholeError::InvalidVAAFormat)?;
             .ok_or(WormholeError::InvalidVAAFormat)?;
         let guardian_set_index = u32::from_be_bytes(
         let guardian_set_index = u32::from_be_bytes(
-            gsi_bytes.try_into()
-                .map_err(|_| WormholeError::InvalidVAAFormat)?
+            gsi_bytes
+                .try_into()
+                .map_err(|_| WormholeError::InvalidVAAFormat)?,
         );
         );
         cursor += 4;
         cursor += 4;
-    
+
         // Get number of signatures
         // Get number of signatures
-        let len_signatures = *encoded_vaa.get(cursor)
+        let len_signatures = *encoded_vaa
+            .get(cursor)
             .ok_or(WormholeError::InvalidVAAFormat)?;
             .ok_or(WormholeError::InvalidVAAFormat)?;
         cursor += 1;
         cursor += 1;
-    
+
         if len_signatures > 19 {
         if len_signatures > 19 {
             return Err(WormholeError::InvalidVAAFormat);
             return Err(WormholeError::InvalidVAAFormat);
         }
         }
-    
+
         let mut signatures = Vec::with_capacity(len_signatures as usize);
         let mut signatures = Vec::with_capacity(len_signatures as usize);
-    
+
         for _ in 0..len_signatures {
         for _ in 0..len_signatures {
             if cursor + 66 > encoded_vaa.len() {
             if cursor + 66 > encoded_vaa.len() {
                 return Err(WormholeError::InvalidVAAFormat);
                 return Err(WormholeError::InvalidVAAFormat);
             }
             }
-    
-            let guardian_index = *encoded_vaa.get(cursor)
+
+            let guardian_index = *encoded_vaa
+                .get(cursor)
                 .ok_or(WormholeError::InvalidVAAFormat)?;
                 .ok_or(WormholeError::InvalidVAAFormat)?;
             cursor += 1;
             cursor += 1;
-    
-            let sig_bytes = encoded_vaa.get(cursor..cursor + 65)
+
+            let sig_bytes = encoded_vaa
+                .get(cursor..cursor + 65)
                 .ok_or(WormholeError::InvalidVAAFormat)?;
                 .ok_or(WormholeError::InvalidVAAFormat)?;
             let mut fixed_sig = [0u8; 65];
             let mut fixed_sig = [0u8; 65];
             fixed_sig.copy_from_slice(sig_bytes);
             fixed_sig.copy_from_slice(sig_bytes);
             cursor += 65;
             cursor += 65;
-    
+
             signatures.push(GuardianSignature {
             signatures.push(GuardianSignature {
                 guardian_index,
                 guardian_index,
                 signature: FixedBytes::from(fixed_sig),
                 signature: FixedBytes::from(fixed_sig),
             });
             });
         }
         }
-    
+
         if cursor + 51 > encoded_vaa.len() {
         if cursor + 51 > encoded_vaa.len() {
             return Err(WormholeError::InvalidVAAFormat);
             return Err(WormholeError::InvalidVAAFormat);
         }
         }
-    
+
         // Get timestamp
         // Get timestamp
-        let ts_bytes = encoded_vaa.get(cursor..cursor + 4)
+        let ts_bytes = encoded_vaa
+            .get(cursor..cursor + 4)
             .ok_or(WormholeError::InvalidVAAFormat)?;
             .ok_or(WormholeError::InvalidVAAFormat)?;
         let timestamp = u32::from_be_bytes(
         let timestamp = u32::from_be_bytes(
-            ts_bytes.try_into()
-                .map_err(|_| WormholeError::InvalidVAAFormat)?
+            ts_bytes
+                .try_into()
+                .map_err(|_| WormholeError::InvalidVAAFormat)?,
         );
         );
         cursor += 4;
         cursor += 4;
-    
+
         // Get nonce
         // Get nonce
-        let nonce_bytes = encoded_vaa.get(cursor..cursor + 4)
+        let nonce_bytes = encoded_vaa
+            .get(cursor..cursor + 4)
             .ok_or(WormholeError::InvalidVAAFormat)?;
             .ok_or(WormholeError::InvalidVAAFormat)?;
         let nonce = u32::from_be_bytes(
         let nonce = u32::from_be_bytes(
-            nonce_bytes.try_into()
-                .map_err(|_| WormholeError::InvalidVAAFormat)?
+            nonce_bytes
+                .try_into()
+                .map_err(|_| WormholeError::InvalidVAAFormat)?,
         );
         );
         cursor += 4;
         cursor += 4;
-    
+
         // Get emitter chain ID
         // Get emitter chain ID
-        let emitter_chain_bytes = encoded_vaa.get(cursor..cursor + 2)
+        let emitter_chain_bytes = encoded_vaa
+            .get(cursor..cursor + 2)
             .ok_or(WormholeError::InvalidVAAFormat)?;
             .ok_or(WormholeError::InvalidVAAFormat)?;
-        let emitter_chain_id = u16::from_be_bytes([
-            emitter_chain_bytes[0],
-            emitter_chain_bytes[1],
-        ]);
+        let emitter_chain_id = u16::from_be_bytes([emitter_chain_bytes[0], emitter_chain_bytes[1]]);
         cursor += 2;
         cursor += 2;
-    
+
         // Get emitter address
         // Get emitter address
-        let emitter_address_bytes = encoded_vaa.get(cursor..cursor + 32)
+        let emitter_address_bytes = encoded_vaa
+            .get(cursor..cursor + 32)
             .ok_or(WormholeError::InvalidVAAFormat)?;
             .ok_or(WormholeError::InvalidVAAFormat)?;
         let mut fixed_emitter = [0u8; 32];
         let mut fixed_emitter = [0u8; 32];
         fixed_emitter.copy_from_slice(emitter_address_bytes);
         fixed_emitter.copy_from_slice(emitter_address_bytes);
         cursor += 32;
         cursor += 32;
-    
+
         // Get sequence
         // Get sequence
-        let sequence_bytes = encoded_vaa.get(cursor..cursor + 8)
+        let sequence_bytes = encoded_vaa
+            .get(cursor..cursor + 8)
             .ok_or(WormholeError::InvalidVAAFormat)?;
             .ok_or(WormholeError::InvalidVAAFormat)?;
         let sequence = u64::from_be_bytes(
         let sequence = u64::from_be_bytes(
-            sequence_bytes.try_into()
-                .map_err(|_| WormholeError::InvalidVAAFormat)?
+            sequence_bytes
+                .try_into()
+                .map_err(|_| WormholeError::InvalidVAAFormat)?,
         );
         );
         cursor += 8;
         cursor += 8;
-    
+
         // Get consistency level
         // Get consistency level
-        let consistency_level = *encoded_vaa.get(cursor)
+        let consistency_level = *encoded_vaa
+            .get(cursor)
             .ok_or(WormholeError::InvalidVAAFormat)?;
             .ok_or(WormholeError::InvalidVAAFormat)?;
         cursor += 1;
         cursor += 1;
-    
+
         // Get payload
         // Get payload
-        let payload = encoded_vaa.get(cursor..)
+        let payload = encoded_vaa
+            .get(cursor..)
             .ok_or(WormholeError::InvalidVAAFormat)?
             .ok_or(WormholeError::InvalidVAAFormat)?
             .to_vec();
             .to_vec();
-    
+
         let hash = keccak256(&encoded_vaa[cursor - 51..]);
         let hash = keccak256(&encoded_vaa[cursor - 51..]);
-    
+
         Ok(VerifiedVM {
         Ok(VerifiedVM {
             version: *version,
             version: *version,
             guardian_set_index,
             guardian_set_index,
@@ -311,20 +328,26 @@ impl WormholeContract {
             hash,
             hash,
         })
         })
     }
     }
-    
 
 
     fn verify_vm(&self, vaa: &VerifiedVM) -> Result<(), WormholeError> {
     fn verify_vm(&self, vaa: &VerifiedVM) -> Result<(), WormholeError> {
         let guardian_set = self.get_gs_internal(vaa.guardian_set_index)?;
         let guardian_set = self.get_gs_internal(vaa.guardian_set_index)?;
-        if vaa.guardian_set_index != self.current_guardian_set_index.get().try_into().unwrap_or(0u32)
-            && guardian_set.expiration_time > 0 {
-                return Err(WormholeError::GuardianSetExpired)
+        let current_gsi = self.current_guardian_set_index.get().try_into().unwrap_or(0u32);
+        if vaa.guardian_set_index != current_gsi && guardian_set.expiration_time > 0 {
+            return Err(WormholeError::GuardianSetExpired);
         }
         }
 
 
-
-        let num_guardians : u32 = guardian_set.keys.len().try_into().map_err(|_| WormholeError::InvalidInput)?;
+        let num_guardians: u32 = guardian_set
+            .keys
+            .len()
+            .try_into()
+            .map_err(|_| WormholeError::InvalidInput)?;
 
 
         let required_signatures = Self::quorum(num_guardians);
         let required_signatures = Self::quorum(num_guardians);
-        let num_signatures : u32 = vaa.signatures.len().try_into().map_err(|_| WormholeError::InvalidInput)?;
+        let num_signatures: u32 = vaa
+            .signatures
+            .len()
+            .try_into()
+            .map_err(|_| WormholeError::InvalidInput)?;
 
 
         if num_signatures < required_signatures {
         if num_signatures < required_signatures {
             return Err(WormholeError::InsufficientSignatures);
             return Err(WormholeError::InsufficientSignatures);
@@ -353,7 +376,7 @@ impl WormholeContract {
             let hashed_vaa_hash: FixedBytes<32> = FixedBytes::from(keccak256(vaa.hash));
             let hashed_vaa_hash: FixedBytes<32> = FixedBytes::from(keccak256(vaa.hash));
 
 
             match self.verify_signature(&hashed_vaa_hash, &signature.signature, guardian_address) {
             match self.verify_signature(&hashed_vaa_hash, &signature.signature, guardian_address) {
-                Ok(true) => {},
+                Ok(true) => {}
                 Ok(false) => return Err(WormholeError::InvalidSignature.into()),
                 Ok(false) => return Err(WormholeError::InvalidSignature.into()),
                 Err(e) => return Err(e),
                 Err(e) => return Err(e),
             }
             }
@@ -367,16 +390,26 @@ impl WormholeContract {
         U256::from_be_bytes(keccak256(&key_data).0)
         U256::from_be_bytes(keccak256(&key_data).0)
     }
     }
 
 
-    fn store_gs(&mut self, set_index: u32, guardians: Vec<Address>, expiration_time: u32) -> Result<(), WormholeError> {
+    fn store_gs(
+        &mut self,
+        set_index: u32,
+        guardians: Vec<Address>,
+        expiration_time: u32,
+    ) -> Result<(), WormholeError> {
         if guardians.is_empty() {
         if guardians.is_empty() {
             return Err(WormholeError::InvalidInput);
             return Err(WormholeError::InvalidInput);
         }
         }
 
 
-        self.guardian_set_sizes.setter(U256::from(set_index)).set(U256::from(guardians.len()));
-        self.guardian_set_expiry.setter(U256::from(set_index)).set(U256::from(expiration_time));
+        self.guardian_set_sizes
+            .setter(U256::from(set_index))
+            .set(U256::from(guardians.len()));
+        self.guardian_set_expiry
+            .setter(U256::from(set_index))
+            .set(U256::from(expiration_time));
 
 
         for (i, guardian) in guardians.iter().enumerate() {
         for (i, guardian) in guardians.iter().enumerate() {
-            let i_u8: u8 = i.try_into()
+            let i_u8: u8 = i
+                .try_into()
                 .map_err(|_| WormholeError::InvalidGuardianIndex)?;
                 .map_err(|_| WormholeError::InvalidGuardianIndex)?;
             let key = self.compute_gs_key(set_index, i_u8);
             let key = self.compute_gs_key(set_index, i_u8);
             self.guardian_keys.setter(key).set(*guardian);
             self.guardian_keys.setter(key).set(*guardian);
@@ -400,27 +433,33 @@ impl WormholeContract {
             RecoveryId::try_from(recovery_id_byte - 27)
             RecoveryId::try_from(recovery_id_byte - 27)
                 .map_err(|_| WormholeError::InvalidSignature)?
                 .map_err(|_| WormholeError::InvalidSignature)?
         } else {
         } else {
-            RecoveryId::try_from(recovery_id_byte)
-                .map_err(|_| WormholeError::InvalidSignature)?
+            RecoveryId::try_from(recovery_id_byte).map_err(|_| WormholeError::InvalidSignature)?
         };
         };
 
 
-        let sig = Signature::try_from(&signature[..64])
-            .map_err(|_| WormholeError::InvalidSignature)?;
+        let sig =
+            Signature::try_from(&signature[..64]).map_err(|_| WormholeError::InvalidSignature)?;
 
 
-        let verifying_key = VerifyingKey::recover_from_prehash(hash.as_slice().try_into().map_err(|_| WormholeError::InvalidInput)?, &sig, recovery_id)
-            .map_err(|_| WormholeError::InvalidSignature)?;
+        let verifying_key = VerifyingKey::recover_from_prehash(
+            hash.as_slice()
+                .try_into()
+                .map_err(|_| WormholeError::InvalidInput)?,
+            &sig,
+            recovery_id,
+        )
+        .map_err(|_| WormholeError::InvalidSignature)?;
 
 
         let public_key_bytes = verifying_key.to_encoded_point(false);
         let public_key_bytes = verifying_key.to_encoded_point(false);
         let public_key_slice = &public_key_bytes.as_bytes()[1..];
         let public_key_slice = &public_key_bytes.as_bytes()[1..];
 
 
         let address_hash = keccak256(public_key_slice);
         let address_hash = keccak256(public_key_slice);
-        let address_bytes: [u8; 20] = address_hash[12..].try_into()
+        let address_bytes: [u8; 20] = address_hash[12..]
+            .try_into()
             .map_err(|_| WormholeError::InvalidAddressLength)?;
             .map_err(|_| WormholeError::InvalidAddressLength)?;
 
 
         Ok(Address::from(address_bytes) == guardian_address)
         Ok(Address::from(address_bytes) == guardian_address)
     }
     }
 
 
-   fn get_gs_internal(&self, index: u32) -> Result<GuardianSet, WormholeError> {
+    fn get_gs_internal(&self, index: u32) -> Result<GuardianSet, WormholeError> {
         let size = self.guardian_set_sizes.getter(U256::from(index)).get();
         let size = self.guardian_set_sizes.getter(U256::from(index)).get();
         if size.is_zero() {
         if size.is_zero() {
             return Err(WormholeError::InvalidGuardianSetIndex);
             return Err(WormholeError::InvalidGuardianSetIndex);
@@ -457,6 +496,7 @@ impl IWormhole for WormholeContract {
 
 
         let vaa = self.parse_vm(&encoded_vaa)?;
         let vaa = self.parse_vm(&encoded_vaa)?;
         self.verify_vm(&vaa)?;
         self.verify_vm(&vaa)?;
+        
         Ok(vaa)
         Ok(vaa)
     }
     }
 
 
@@ -465,14 +505,16 @@ impl IWormhole for WormholeContract {
     }
     }
 
 
     fn get_current_guardian_set_index(&self) -> u32 {
     fn get_current_guardian_set_index(&self) -> u32 {
-        self.current_guardian_set_index.get().try_into().unwrap_or(0u32)
+        self.current_guardian_set_index
+            .get()
+            .try_into()
+            .unwrap_or(0u32)
     }
     }
 
 
     fn governance_action_is_consumed(&self, hash: Vec<u8>) -> bool {
     fn governance_action_is_consumed(&self, hash: Vec<u8>) -> bool {
         self.consumed_governance_actions.get(hash)
         self.consumed_governance_actions.get(hash)
     }
     }
 
 
-    #[inline]
     fn chain_id(&self) -> u16 {
     fn chain_id(&self) -> u16 {
         self.chain_id.get().try_into().unwrap_or(0u16)
         self.chain_id.get().try_into().unwrap_or(0u16)
     }
     }
@@ -502,10 +544,10 @@ mod tests {
     use k256::ecdsa::SigningKey;
     use k256::ecdsa::SigningKey;
     use stylus_sdk::alloy_primitives::keccak256;
     use stylus_sdk::alloy_primitives::keccak256;
 
 
-    #[cfg(test)]
-    use base64::engine::general_purpose;
     #[cfg(test)]
     #[cfg(test)]
     use base64::Engine;
     use base64::Engine;
+    #[cfg(test)]
+    use base64::engine::general_purpose;
 
 
     const CHAIN_ID: u16 = 60051;
     const CHAIN_ID: u16 = 60051;
     const GOVERNANCE_CHAIN_ID: u16 = 1;
     const GOVERNANCE_CHAIN_ID: u16 = 1;
@@ -518,9 +560,7 @@ mod tests {
 
 
     #[cfg(test)]
     #[cfg(test)]
     fn create_vaa_bytes(input_string: &str) -> Vec<u8> {
     fn create_vaa_bytes(input_string: &str) -> Vec<u8> {
-        let vaa_bytes = general_purpose::STANDARD
-            .decode(input_string)
-            .unwrap();
+        let vaa_bytes = general_purpose::STANDARD.decode(input_string).unwrap();
         let vaa: Vec<u8> = vaa_bytes;
         let vaa: Vec<u8> = vaa_bytes;
         vaa
         vaa
     }
     }
@@ -528,20 +568,18 @@ mod tests {
     #[cfg(test)]
     #[cfg(test)]
     fn test_guardian_secret1() -> [u8; 32] {
     fn test_guardian_secret1() -> [u8; 32] {
         [
         [
-            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
-            0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
-            0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
-            0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+            0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
+            0x1d, 0x1e, 0x1f, 0x20,
         ]
         ]
     }
     }
 
 
     #[cfg(test)]
     #[cfg(test)]
     fn test_guardian_secret2() -> [u8; 32] {
     fn test_guardian_secret2() -> [u8; 32] {
         [
         [
-            0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
-            0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
-            0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
-            0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40,
+            0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e,
+            0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c,
+            0x3d, 0x3e, 0x3f, 0x40,
         ]
         ]
     }
     }
 
 
@@ -596,8 +634,6 @@ mod tests {
         ]
         ]
     }
     }
 
 
-
-
     #[cfg(test)]
     #[cfg(test)]
     fn test_guardian_address1() -> Address {
     fn test_guardian_address1() -> Address {
         let secret = test_guardian_secret1();
         let secret = test_guardian_secret1();
@@ -610,7 +646,6 @@ mod tests {
         Address::from(address_bytes)
         Address::from(address_bytes)
     }
     }
 
 
-
     #[cfg(test)]
     #[cfg(test)]
     fn test_guardian_address2() -> Address {
     fn test_guardian_address2() -> Address {
         let secret = test_guardian_secret2();
         let secret = test_guardian_secret2();
@@ -677,8 +712,14 @@ mod tests {
     #[cfg(test)]
     #[cfg(test)]
     fn mock_guardian_set13() -> Vec<Address> {
     fn mock_guardian_set13() -> Vec<Address> {
         vec![
         vec![
-            Address::from([0x58, 0x93, 0xB5, 0xA7, 0x6c, 0x3f, 0x73, 0x96, 0x45, 0x64, 0x88, 0x85, 0xbD, 0xCc, 0xC0, 0x6c, 0xd7, 0x0a, 0x3C, 0xd3]),
-            Address::from([0xff, 0x6C, 0xB9, 0x52, 0x58, 0x9B, 0xDE, 0x86, 0x2c, 0x25, 0xEf, 0x43, 0x92, 0x13, 0x2f, 0xb9, 0xD4, 0xA4, 0x21, 0x57]),
+            Address::from([
+                0x58, 0x93, 0xB5, 0xA7, 0x6c, 0x3f, 0x73, 0x96, 0x45, 0x64, 0x88, 0x85, 0xbD, 0xCc,
+                0xC0, 0x6c, 0xd7, 0x0a, 0x3C, 0xd3,
+            ]),
+            Address::from([
+                0xff, 0x6C, 0xB9, 0x52, 0x58, 0x9B, 0xDE, 0x86, 0x2c, 0x25, 0xEf, 0x43, 0x92, 0x13,
+                0x2f, 0xb9, 0xD4, 0xA4, 0x21, 0x57,
+            ]),
         ]
         ]
     }
     }
 
 
@@ -711,7 +752,11 @@ mod tests {
         }
         }
     }
     }
 
 
-    fn create_test_vaa_with_emitter(guardian_set_index: u32, signatures: Vec<GuardianSignature>, emitter: Address) -> VerifiedVM {
+    fn create_test_vaa_with_emitter(
+        guardian_set_index: u32,
+        signatures: Vec<GuardianSignature>,
+        emitter: Address,
+    ) -> VerifiedVM {
         let mut emitter_bytes = [0u8; 32];
         let mut emitter_bytes = [0u8; 32];
         emitter_bytes[12..32].copy_from_slice(emitter.as_slice());
         emitter_bytes[12..32].copy_from_slice(emitter.as_slice());
 
 
@@ -730,7 +775,10 @@ mod tests {
         }
         }
     }
     }
 
 
-    fn create_valid_guardian_signature(guardian_index: u8, hash: &FixedBytes<32>) -> Result<GuardianSignature, WormholeError> {
+    fn create_valid_guardian_signature(
+        guardian_index: u8,
+        hash: &FixedBytes<32>,
+    ) -> Result<GuardianSignature, WormholeError> {
         let secret_bytes = match guardian_index {
         let secret_bytes = match guardian_index {
             0 => test_guardian_secret1(),
             0 => test_guardian_secret1(),
             1 => test_guardian_secret2(),
             1 => test_guardian_secret2(),
@@ -740,10 +788,13 @@ mod tests {
         let signing_key = SigningKey::from_bytes(&secret_bytes.into())
         let signing_key = SigningKey::from_bytes(&secret_bytes.into())
             .map_err(|_| WormholeError::InvalidInput)?;
             .map_err(|_| WormholeError::InvalidInput)?;
 
 
-        let hash_array: [u8; 32] = hash.as_slice().try_into()
+        let hash_array: [u8; 32] = hash
+            .as_slice()
+            .try_into()
             .map_err(|_| WormholeError::InvalidInput)?;
             .map_err(|_| WormholeError::InvalidInput)?;
 
 
-        let (signature, recovery_id) = signing_key.sign_prehash_recoverable(&hash_array)
+        let (signature, recovery_id) = signing_key
+            .sign_prehash_recoverable(&hash_array)
             .map_err(|_| WormholeError::InvalidInput)?;
             .map_err(|_| WormholeError::InvalidInput)?;
 
 
         let mut sig_bytes = [0u8; 65];
         let mut sig_bytes = [0u8; 65];
@@ -1113,7 +1164,6 @@ mod tests {
         let gov_contract = wormhole_contract.sender(alice).governance_contract();
         let gov_contract = wormhole_contract.sender(alice).governance_contract();
         let expected = Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
         let expected = Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
         assert_eq!(gov_contract, expected);
         assert_eq!(gov_contract, expected);
-
     }
     }
 
 
     #[motsu::test]
     #[motsu::test]
@@ -1127,7 +1177,8 @@ mod tests {
     #[motsu::test]
     #[motsu::test]
     fn test_initialize_contract_like_shell_script(wormhole_contract: Contract<WormholeContract>, alice: Address) {
     fn test_initialize_contract_like_shell_script(wormhole_contract: Contract<WormholeContract>, alice: Address) {
         let guardians = current_guardians();
         let guardians = current_guardians();
-        let governance_contract = Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
+        let governance_contract =
+            Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
 
 
         let result = wormhole_contract.sender(alice).initialize(guardians.clone(), 4, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract);
         let result = wormhole_contract.sender(alice).initialize(guardians.clone(), 4, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract);
         assert!(result.is_ok(), "Contract initialization should succeed");
         assert!(result.is_ok(), "Contract initialization should succeed");
@@ -1136,13 +1187,17 @@ mod tests {
     #[motsu::test]
     #[motsu::test]
     fn test_quorum_calculation_integration_test(wormhole_contract: Contract<WormholeContract>, alice: Address) {
     fn test_quorum_calculation_integration_test(wormhole_contract: Contract<WormholeContract>, alice: Address) {
         let quorum_result = WormholeContract::quorum(3);
         let quorum_result = WormholeContract::quorum(3);
-        assert_eq!(quorum_result, 3, "Quorum calculation should work: (3 * 2) / 3 + 1 = 3");
+        assert_eq!(
+            quorum_result, 3,
+            "Quorum calculation should work: (3 * 2) / 3 + 1 = 3"
+        );
     }
     }
 
 
     #[motsu::test]
     #[motsu::test]
     fn test_guardian_set_retrieval_current_guardians(wormhole_contract: Contract<WormholeContract>, alice: Address) {
     fn test_guardian_set_retrieval_current_guardians(wormhole_contract: Contract<WormholeContract>, alice: Address) {
         let guardians = current_guardians();
         let guardians = current_guardians();
-        let governance_contract = Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
+        let governance_contract =
+            Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
 
 
         let _ = wormhole_contract.sender(alice).initialize(guardians.clone(), 4, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract);
         let _ = wormhole_contract.sender(alice).initialize(guardians.clone(), 4, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract);
 
 
@@ -1150,7 +1205,11 @@ mod tests {
         assert!(guardian_set_result.is_ok(), "Guardian set retrieval should work - contract is initialized");
         assert!(guardian_set_result.is_ok(), "Guardian set retrieval should work - contract is initialized");
 
 
         let guardian_set_bytes = guardian_set_result.unwrap();
         let guardian_set_bytes = guardian_set_result.unwrap();
-        assert_eq!(guardian_set_bytes.len(), 19 * 20, "Should have 19 guardian addresses (20 bytes each)");
+        assert_eq!(
+            guardian_set_bytes.len(),
+            19 * 20,
+            "Should have 19 guardian addresses (20 bytes each)"
+        );
 
 
         assert_eq!(wormhole_contract.sender(alice).chain_id(), CHAIN_ID, "Chain ID should match shell script value");
         assert_eq!(wormhole_contract.sender(alice).chain_id(), CHAIN_ID, "Chain ID should match shell script value");
 
 
@@ -1164,7 +1223,8 @@ mod tests {
     #[motsu::test]
     #[motsu::test]
     fn test_duplicate_verification(wormhole_contract: Contract<WormholeContract>, alice: Address) {
     fn test_duplicate_verification(wormhole_contract: Contract<WormholeContract>, alice: Address) {
         let guardians = current_guardians_duplicate();
         let guardians = current_guardians_duplicate();
-        let governance_contract = Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
+        let governance_contract =
+            Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
 
 
         let _ = wormhole_contract.sender(alice).initialize(guardians.clone(), 4, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract);
         let _ = wormhole_contract.sender(alice).initialize(guardians.clone(), 4, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract);
 
 
@@ -1179,7 +1239,8 @@ mod tests {
     #[motsu::test]
     #[motsu::test]
     fn switch_guardian_set(wormhole_contract: Contract<WormholeContract>, alice: Address) {
     fn switch_guardian_set(wormhole_contract: Contract<WormholeContract>, alice: Address) {
         let guardians = current_guardians_duplicate();
         let guardians = current_guardians_duplicate();
-        let governance_contract = Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
+        let governance_contract =
+            Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
 
 
         let _ = wormhole_contract.sender(alice).initialize(guardians.clone(), 3, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract);
         let _ = wormhole_contract.sender(alice).initialize(guardians.clone(), 3, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract);
         let test_vaa = create_vaa_bytes("AQAAAAQNAInUwKI1ItLfYeLaAibn9oXaouTs9BL3Aa9DKCFWrLu0KDaQQMQJlih0Qh7l7yH2o6kD/g9RCmRwZJ6q0OZE0t4AArCSH1wpX04N1U59tQmss2xXZilimAMKlogp7ErAhAo0LFkDogqB74+2By9rm3P5OUWlbC0lrFNut5CQQV38DGsAAxO+1nUTUc842P2afDSjdWcmjvJl2s8secQzuiW8zrdgPpbzhzWsiYXizLQBRKigDS8pWGD4vRk0fuR8H/ZkO/0BBOmDobl1BLNJx7+Pt+NWfuOUBipVFIXGxI9b3vxxH0BIec8hhxDN4m2Pd2I0klGEXKhv9plcR7VlzAsaC7ZE7QIABh4ff66tP7EHdVfZR4mTzv5B97agMcSB1eDeijpyl9JuBhbMupw7nExZNnZag/x2k6AUEWnQnfp8AoaCK7Av+icAB2Ouk9mPd1ybyju39Q8m7GMevt2f1nHVyWVsPRzdEcCuAbzjh5137DCLzVWuFUujTQJ7IJiznQb6cm2Ljk3WOXUACMa/JwRdpVKZf6eTD6O6tivqhdhMtbijlPBZX/kgVKk5Xuyv3h1SRTrNCwkMg5XOWegnCbXqjbUlo+F3qTjCalQBCxfp1itJskZmv+SXA47QivURKWzGa3mntNh0vcAXYi8FeChvoUYmfYpejmBlOkD1I73pmUsyrbYbetHa7qFu3eoBDZScdyrWp2dS5Y9L4b0who/PncVp5oFs/4J8ThHNQoXWXvys+nUc2aM+E+Fwazo2ODdI8XZz9YOGf/ZfE6iXFBYBDgckow8Nb2QD//C6MfP2Bz8zftqvt+D6Dko7v/Inb2OtCj342yjrxcvAMlCQ6lYoTIAMNemzNoqlfNyDMdB9yKoAEKebRtCm8QZSjLQ5uPk8aoQpmNwCpLhiHuzh2fqH55fcQrE6/KFttfw7VzeGUE7k3PF6xIMq0BPr3vkG2MedIh8BEQvpmYK4fChLY5JG26Kk6KuZ1eCkJAOQgdSjWasAvNgsSIlsb5mFjIkGwK9j20svLSl+OJ7I0olefXcZ2JywjgYAEu1jITMLHCMR1blXENulhApdhMfTef1aQ/USMqRVWNigausEzq49Hi2GtcQzHmZuhgnhBZEnjq9K8jsZwJk59iwBaFxZegAAAAAAATTNxrJiPzbWCugg6Vtg92ToHsLNO1e3fj+OJd3UOsNzAAAAAAATpFIAAVE6cNLnZT2Noq5nJ4VNRSf2KrRBNrlimFaXauHv3efDAAFm5RiKEwih25C20x8/vcqMPfJnjIES3909GSxaPMRXqAAAAAAAAAAAAAAAAFxIFHGlrpnuxd5M5WePQalLpUyHAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALFcwAAAAAAAAAAAAAAAAaFxdzQAAAAAAAAAAAAAAAK3MabLDE8LWvGN6+AdUvFHJdm5RAAMAAAAAAAAAAAAAAADf0SJhChSsEtk0iYwC2+wfcnCBFg==");
         let test_vaa = create_vaa_bytes("AQAAAAQNAInUwKI1ItLfYeLaAibn9oXaouTs9BL3Aa9DKCFWrLu0KDaQQMQJlih0Qh7l7yH2o6kD/g9RCmRwZJ6q0OZE0t4AArCSH1wpX04N1U59tQmss2xXZilimAMKlogp7ErAhAo0LFkDogqB74+2By9rm3P5OUWlbC0lrFNut5CQQV38DGsAAxO+1nUTUc842P2afDSjdWcmjvJl2s8secQzuiW8zrdgPpbzhzWsiYXizLQBRKigDS8pWGD4vRk0fuR8H/ZkO/0BBOmDobl1BLNJx7+Pt+NWfuOUBipVFIXGxI9b3vxxH0BIec8hhxDN4m2Pd2I0klGEXKhv9plcR7VlzAsaC7ZE7QIABh4ff66tP7EHdVfZR4mTzv5B97agMcSB1eDeijpyl9JuBhbMupw7nExZNnZag/x2k6AUEWnQnfp8AoaCK7Av+icAB2Ouk9mPd1ybyju39Q8m7GMevt2f1nHVyWVsPRzdEcCuAbzjh5137DCLzVWuFUujTQJ7IJiznQb6cm2Ljk3WOXUACMa/JwRdpVKZf6eTD6O6tivqhdhMtbijlPBZX/kgVKk5Xuyv3h1SRTrNCwkMg5XOWegnCbXqjbUlo+F3qTjCalQBCxfp1itJskZmv+SXA47QivURKWzGa3mntNh0vcAXYi8FeChvoUYmfYpejmBlOkD1I73pmUsyrbYbetHa7qFu3eoBDZScdyrWp2dS5Y9L4b0who/PncVp5oFs/4J8ThHNQoXWXvys+nUc2aM+E+Fwazo2ODdI8XZz9YOGf/ZfE6iXFBYBDgckow8Nb2QD//C6MfP2Bz8zftqvt+D6Dko7v/Inb2OtCj342yjrxcvAMlCQ6lYoTIAMNemzNoqlfNyDMdB9yKoAEKebRtCm8QZSjLQ5uPk8aoQpmNwCpLhiHuzh2fqH55fcQrE6/KFttfw7VzeGUE7k3PF6xIMq0BPr3vkG2MedIh8BEQvpmYK4fChLY5JG26Kk6KuZ1eCkJAOQgdSjWasAvNgsSIlsb5mFjIkGwK9j20svLSl+OJ7I0olefXcZ2JywjgYAEu1jITMLHCMR1blXENulhApdhMfTef1aQ/USMqRVWNigausEzq49Hi2GtcQzHmZuhgnhBZEnjq9K8jsZwJk59iwBaFxZegAAAAAAATTNxrJiPzbWCugg6Vtg92ToHsLNO1e3fj+OJd3UOsNzAAAAAAATpFIAAVE6cNLnZT2Noq5nJ4VNRSf2KrRBNrlimFaXauHv3efDAAFm5RiKEwih25C20x8/vcqMPfJnjIES3909GSxaPMRXqAAAAAAAAAAAAAAAAFxIFHGlrpnuxd5M5WePQalLpUyHAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALFcwAAAAAAAAAAAAAAAAaFxdzQAAAAAAAAAAAAAAAK3MabLDE8LWvGN6+AdUvFHJdm5RAAMAAAAAAAAAAAAAAADf0SJhChSsEtk0iYwC2+wfcnCBFg==");
@@ -1199,6 +1260,4 @@ mod tests {
         let result2 = wormhole_contract.sender(alice).parse_and_verify_vm(test_vaa.clone());
         let result2 = wormhole_contract.sender(alice).parse_and_verify_vm(test_vaa.clone());
         assert!(result2.is_ok());
         assert!(result2.is_ok());
     }
     }
-
-
 }
 }

+ 40 - 30
target_chains/stylus/contracts/wormhole/tests/integration_test.rs

@@ -5,10 +5,12 @@ use core::str::FromStr;
 use stylus_sdk::alloy_primitives::{Address, FixedBytes, U256};
 use stylus_sdk::alloy_primitives::{Address, FixedBytes, U256};
 use motsu::prelude::Contract;
 use motsu::prelude::Contract;
 use k256::ecdsa::SigningKey;
 use k256::ecdsa::SigningKey;
+use motsu::prelude::DefaultStorage;
 use stylus_sdk::alloy_primitives::keccak256;
 use stylus_sdk::alloy_primitives::keccak256;
+use wormhole_contract::*;
 
 
-use base64::engine::general_purpose;
 use base64::Engine;
 use base64::Engine;
+use base64::engine::general_purpose;
 
 
 const CHAIN_ID: u16 = 60051;
 const CHAIN_ID: u16 = 60051;
 const GOVERNANCE_CHAIN_ID: u16 = 1;
 const GOVERNANCE_CHAIN_ID: u16 = 1;
@@ -19,28 +21,24 @@ fn test_wormhole_vaa() -> Vec<u8> {
 }
 }
 
 
 fn create_vaa_bytes(input_string: &str) -> Vec<u8> {
 fn create_vaa_bytes(input_string: &str) -> Vec<u8> {
-    let vaa_bytes = general_purpose::STANDARD
-        .decode(input_string)
-        .unwrap();
+    let vaa_bytes = general_purpose::STANDARD.decode(input_string).unwrap();
     let vaa: Vec<u8> = vaa_bytes;
     let vaa: Vec<u8> = vaa_bytes;
     vaa
     vaa
 }
 }
 
 
 fn test_guardian_secret1() -> [u8; 32] {
 fn test_guardian_secret1() -> [u8; 32] {
     [
     [
-        0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
-        0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
-        0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
-        0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+        0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+        0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
+        0x1f, 0x20,
     ]
     ]
 }
 }
 
 
 fn test_guardian_secret2() -> [u8; 32] {
 fn test_guardian_secret2() -> [u8; 32] {
     [
     [
-        0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
-        0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
-        0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
-        0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40,
+        0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+        0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
+        0x3f, 0x40,
     ]
     ]
 }
 }
 
 
@@ -104,8 +102,6 @@ fn test_guardian_address1() -> Address {
     Address::from(address_bytes)
     Address::from(address_bytes)
 }
 }
 
 
-
-
 fn test_guardian_address2() -> Address {
 fn test_guardian_address2() -> Address {
     let secret = test_guardian_secret2();
     let secret = test_guardian_secret2();
     let signing_key = SigningKey::from_bytes(&secret.into()).expect("key");
     let signing_key = SigningKey::from_bytes(&secret.into()).expect("key");
@@ -145,12 +141,10 @@ fn deploy_with_mainnet_guardians(wormhole_contract: &Contract<WormholeContract>,
     wormhole_contract.sender(*alice).initialize(guardians, 0, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract).unwrap();
     wormhole_contract.sender(*alice).initialize(guardians, 0, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract).unwrap();
 }
 }
 
 
-
 fn guardian_set0() -> Vec<Address> {
 fn guardian_set0() -> Vec<Address> {
     vec![Address::from_str("0x58CC3AE5C097b213cE3c81979e1B9f9570746AA5").unwrap()]
     vec![Address::from_str("0x58CC3AE5C097b213cE3c81979e1B9f9570746AA5").unwrap()]
 }
 }
 
 
-
 fn guardian_set4() -> Vec<Address> {
 fn guardian_set4() -> Vec<Address> {
     vec![
     vec![
         Address::from_str("0x5893B5A76c3f739645648885bDCcC06cd70a3Cd3").unwrap(),
         Address::from_str("0x5893B5A76c3f739645648885bDCcC06cd70a3Cd3").unwrap(),
@@ -159,15 +153,19 @@ fn guardian_set4() -> Vec<Address> {
     ]
     ]
 }
 }
 
 
-
 fn mock_guardian_set13() -> Vec<Address> {
 fn mock_guardian_set13() -> Vec<Address> {
     vec![
     vec![
-        Address::from([0x58, 0x93, 0xB5, 0xA7, 0x6c, 0x3f, 0x73, 0x96, 0x45, 0x64, 0x88, 0x85, 0xbD, 0xCc, 0xC0, 0x6c, 0xd7, 0x0a, 0x3C, 0xd3]),
-        Address::from([0xff, 0x6C, 0xB9, 0x52, 0x58, 0x9B, 0xDE, 0x86, 0x2c, 0x25, 0xEf, 0x43, 0x92, 0x13, 0x2f, 0xb9, 0xD4, 0xA4, 0x21, 0x57]),
+        Address::from([
+            0x58, 0x93, 0xB5, 0xA7, 0x6c, 0x3f, 0x73, 0x96, 0x45, 0x64, 0x88, 0x85, 0xbD, 0xCc,
+            0xC0, 0x6c, 0xd7, 0x0a, 0x3C, 0xd3,
+        ]),
+        Address::from([
+            0xff, 0x6C, 0xB9, 0x52, 0x58, 0x9B, 0xDE, 0x86, 0x2c, 0x25, 0xEf, 0x43, 0x92, 0x13,
+            0x2f, 0xb9, 0xD4, 0xA4, 0x21, 0x57,
+        ]),
     ]
     ]
 }
 }
 
 
-
 fn corrupted_vaa(mut real_data: Vec<u8>, pos: usize, random1: u8, random2: u8) -> Vec<u8> {
 fn corrupted_vaa(mut real_data: Vec<u8>, pos: usize, random1: u8, random2: u8) -> Vec<u8> {
     if real_data.len() < 2 {
     if real_data.len() < 2 {
         return real_data;
         return real_data;
@@ -196,7 +194,11 @@ fn create_test_vaa(guardian_set_index: u32, signatures: Vec<GuardianSignature>)
     }
     }
 }
 }
 
 
-fn create_test_vaa_with_emitter(guardian_set_index: u32, signatures: Vec<GuardianSignature>, emitter: Address) -> VerifiedVM {
+fn create_test_vaa_with_emitter(
+    guardian_set_index: u32,
+    signatures: Vec<GuardianSignature>,
+    emitter: Address,
+) -> VerifiedVM {
     let mut emitter_bytes = [0u8; 32];
     let mut emitter_bytes = [0u8; 32];
     emitter_bytes[12..32].copy_from_slice(emitter.as_slice());
     emitter_bytes[12..32].copy_from_slice(emitter.as_slice());
 
 
@@ -215,20 +217,26 @@ fn create_test_vaa_with_emitter(guardian_set_index: u32, signatures: Vec<Guardia
     }
     }
 }
 }
 
 
-fn create_valid_guardian_signature(guardian_index: u8, hash: &FixedBytes<32>) -> Result<GuardianSignature, WormholeError> {
+fn create_valid_guardian_signature(
+    guardian_index: u8,
+    hash: &FixedBytes<32>,
+) -> Result<GuardianSignature, WormholeError> {
     let secret_bytes = match guardian_index {
     let secret_bytes = match guardian_index {
         0 => test_guardian_secret1(),
         0 => test_guardian_secret1(),
         1 => test_guardian_secret2(),
         1 => test_guardian_secret2(),
         _ => test_guardian_secret1(),
         _ => test_guardian_secret1(),
     };
     };
 
 
-    let signing_key = SigningKey::from_bytes(&secret_bytes.into())
-        .map_err(|_| WormholeError::InvalidInput)?;
+    let signing_key =
+        SigningKey::from_bytes(&secret_bytes.into()).map_err(|_| WormholeError::InvalidInput)?;
 
 
-    let hash_array: [u8; 32] = hash.as_slice().try_into()
+    let hash_array: [u8; 32] = hash
+        .as_slice()
+        .try_into()
         .map_err(|_| WormholeError::InvalidInput)?;
         .map_err(|_| WormholeError::InvalidInput)?;
 
 
-    let (signature, recovery_id) = signing_key.sign_prehash_recoverable(&hash_array)
+    let (signature, recovery_id) = signing_key
+        .sign_prehash_recoverable(&hash_array)
         .map_err(|_| WormholeError::InvalidInput)?;
         .map_err(|_| WormholeError::InvalidInput)?;
 
 
     let mut sig_bytes = [0u8; 65];
     let mut sig_bytes = [0u8; 65];
@@ -241,7 +249,6 @@ fn create_valid_guardian_signature(guardian_index: u8, hash: &FixedBytes<32>) ->
     })
     })
 }
 }
 
 
-
 fn create_guardian_signature(guardian_index: u8) -> GuardianSignature {
 fn create_guardian_signature(guardian_index: u8) -> GuardianSignature {
     GuardianSignature {
     GuardianSignature {
         guardian_index,
         guardian_index,
@@ -353,7 +360,6 @@ fn test_chain_id_governance_values(wormhole_contract: Contract<WormholeContract>
     let gov_contract = wormhole_contract.sender(alice).governance_contract();
     let gov_contract = wormhole_contract.sender(alice).governance_contract();
     let expected = Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
     let expected = Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
     assert_eq!(gov_contract, expected);
     assert_eq!(gov_contract, expected);
-
 }
 }
 
 
 #[motsu::test]
 #[motsu::test]
@@ -384,7 +390,11 @@ fn test_guardian_set_retrieval_current_guardians(wormhole_contract: Contract<Wor
     assert!(guardian_set_result.is_ok(), "Guardian set retrieval should work - contract is initialized");
     assert!(guardian_set_result.is_ok(), "Guardian set retrieval should work - contract is initialized");
 
 
     let guardian_set_bytes = guardian_set_result.unwrap();
     let guardian_set_bytes = guardian_set_result.unwrap();
-    assert_eq!(guardian_set_bytes.len(), 19 * 20, "Should have 19 guardian addresses (20 bytes each)");
+    assert_eq!(
+        guardian_set_bytes.len(),
+        19 * 20,
+        "Should have 19 guardian addresses (20 bytes each)"
+    );
 
 
     assert_eq!(wormhole_contract.sender(alice).chain_id(), CHAIN_ID, "Chain ID should match shell script value");
     assert_eq!(wormhole_contract.sender(alice).chain_id(), CHAIN_ID, "Chain ID should match shell script value");
 
 
@@ -409,4 +419,4 @@ fn test_duplicate_verification(wormhole_contract: Contract<WormholeContract>, al
     let result = wormhole_contract.sender(alice).parse_and_verify_vm(test_vaa);
     let result = wormhole_contract.sender(alice).parse_and_verify_vm(test_vaa);
     println!("result: {:?}", result);
     println!("result: {:?}", result);
     assert!(result.is_err());
     assert!(result.is_err());
-}
+}