use pyth::reader::ReaderTrait; use core::array::ArrayTrait; use pyth::reader::{Reader, ReaderImpl}; use pyth::byte_buffer::ByteBuffer; use pyth::pyth::errors::GovernanceActionError; use core::panic_with_felt252; use core::starknet::{ContractAddress, ClassHash}; use super::DataSource; const MAGIC: u32 = 0x5054474d; const MODULE_TARGET: u8 = 1; #[derive(Drop, Copy, Debug, PartialEq, Serde, Hash)] pub enum GovernanceAction { UpgradeContract, AuthorizeGovernanceDataSourceTransfer, SetDataSources, SetFee, SetValidPeriod, RequestGovernanceDataSourceTransfer, SetWormholeAddress, SetFeeInToken, } impl U8TryIntoGovernanceAction of TryInto { fn try_into(self: u8) -> Option { let v = match self { 0 => GovernanceAction::UpgradeContract, 1 => GovernanceAction::AuthorizeGovernanceDataSourceTransfer, 2 => GovernanceAction::SetDataSources, 3 => GovernanceAction::SetFee, 4 => GovernanceAction::SetValidPeriod, 5 => GovernanceAction::RequestGovernanceDataSourceTransfer, 6 => GovernanceAction::SetWormholeAddress, 7 => GovernanceAction::SetFeeInToken, _ => { return Option::None; } }; Option::Some(v) } } #[derive(Drop, Clone, Debug, PartialEq, Serde)] pub struct GovernanceInstruction { pub target_chain_id: u16, pub payload: GovernancePayload, } #[derive(Drop, Clone, Debug, PartialEq, Serde)] pub enum GovernancePayload { UpgradeContract: UpgradeContract, AuthorizeGovernanceDataSourceTransfer: AuthorizeGovernanceDataSourceTransfer, SetDataSources: SetDataSources, SetFee: SetFee, // SetValidPeriod is unsupported RequestGovernanceDataSourceTransfer: RequestGovernanceDataSourceTransfer, SetWormholeAddress: SetWormholeAddress, SetFeeInToken: SetFeeInToken, } #[derive(Drop, Clone, Debug, PartialEq, Serde)] pub struct SetFee { pub value: u64, pub expo: u64, } #[derive(Drop, Clone, Debug, PartialEq, Serde)] pub struct SetFeeInToken { pub value: u64, pub expo: u64, pub token: ContractAddress, } #[derive(Drop, Clone, Debug, PartialEq, Serde)] pub struct SetDataSources { pub sources: Array, } #[derive(Drop, Clone, Debug, PartialEq, Serde)] pub struct SetWormholeAddress { pub address: ContractAddress, } #[derive(Drop, Clone, Debug, PartialEq, Serde)] pub struct RequestGovernanceDataSourceTransfer { // Index is used to prevent replay attacks // So a claimVaa cannot be used twice. pub governance_data_source_index: u32, } #[derive(Drop, Clone, Debug, PartialEq, Serde)] pub struct AuthorizeGovernanceDataSourceTransfer { // Transfer governance control over this contract to another data source. // The claim_vaa field is a VAA created by the new data source; using a VAA prevents mistakes // in the handoff by ensuring that the new data source can send VAAs (i.e., is not an invalid address). pub claim_vaa: ByteBuffer, } #[derive(Drop, Clone, Debug, PartialEq, Serde)] pub struct UpgradeContract { // Class hash of the new contract class. The contract class must already be deployed on the network // (e.g. with `starkli declare`). Class hash is a Poseidon hash of all properties // of the contract code, including entry points, ABI, and bytecode, // so specifying a hash securely identifies the new implementation. pub new_implementation: ClassHash, } pub fn parse_instruction(payload: ByteBuffer) -> GovernanceInstruction { let mut reader = ReaderImpl::new(payload); let magic = reader.read_u32(); if magic != MAGIC { panic_with_felt252(GovernanceActionError::InvalidGovernanceMessage.into()); } let module = reader.read_u8(); if module != MODULE_TARGET { panic_with_felt252(GovernanceActionError::InvalidGovernanceTarget.into()); } let action: GovernanceAction = reader .read_u8() .try_into() .expect(GovernanceActionError::InvalidGovernanceMessage.into()); let target_chain_id = reader.read_u16(); let payload = match action { GovernanceAction::UpgradeContract => { let new_implementation: felt252 = reader .read_u256() .try_into() .expect(GovernanceActionError::InvalidGovernanceMessage.into()); if new_implementation == 0 { panic_with_felt252(GovernanceActionError::InvalidGovernanceMessage.into()); } let new_implementation = new_implementation .try_into() .expect(GovernanceActionError::InvalidGovernanceMessage.into()); GovernancePayload::UpgradeContract(UpgradeContract { new_implementation }) }, GovernanceAction::AuthorizeGovernanceDataSourceTransfer => { let len = reader.len(); let claim_vaa = reader.read_byte_array(len); GovernancePayload::AuthorizeGovernanceDataSourceTransfer( AuthorizeGovernanceDataSourceTransfer { claim_vaa } ) }, GovernanceAction::RequestGovernanceDataSourceTransfer => { let governance_data_source_index = reader.read_u32(); GovernancePayload::RequestGovernanceDataSourceTransfer( RequestGovernanceDataSourceTransfer { governance_data_source_index } ) }, GovernanceAction::SetDataSources => { let num_sources = reader.read_u8(); let mut i = 0; let mut sources = array![]; while i < num_sources { let emitter_chain_id = reader.read_u16(); let emitter_address = reader.read_u256(); sources.append(DataSource { emitter_chain_id, emitter_address }); i += 1; }; GovernancePayload::SetDataSources(SetDataSources { sources }) }, GovernanceAction::SetFee => { let value = reader.read_u64(); let expo = reader.read_u64(); GovernancePayload::SetFee(SetFee { value, expo }) }, GovernanceAction::SetFeeInToken => { let value = reader.read_u64(); let expo = reader.read_u64(); let token_len = reader.read_u8(); if token_len != 32 { panic_with_felt252(GovernanceActionError::InvalidGovernanceMessage.into()); } let token: felt252 = reader .read_u256() .try_into() .expect(GovernanceActionError::InvalidGovernanceMessage.into()); let token = token .try_into() .expect(GovernanceActionError::InvalidGovernanceMessage.into()); GovernancePayload::SetFeeInToken(SetFeeInToken { value, expo, token }) }, GovernanceAction::SetValidPeriod => { panic_with_felt252('unimplemented') }, GovernanceAction::SetWormholeAddress => { let address: felt252 = reader .read_u256() .try_into() .expect(GovernanceActionError::InvalidGovernanceMessage.into()); let address = address .try_into() .expect(GovernanceActionError::InvalidGovernanceMessage.into()); GovernancePayload::SetWormholeAddress(SetWormholeAddress { address }) }, }; if reader.len() != 0 { panic_with_felt252(GovernanceActionError::InvalidGovernanceMessage.into()); } GovernanceInstruction { target_chain_id, payload, } }