contract;
use std::{
asset_id::AssetId,
block::timestamp,
bytes::Bytes,
call_frames::msg_asset_id,
constants::{
ZERO_B256,
},
context::msg_amount,
hash::{
Hash,
keccak256,
sha256,
},
revert::revert,
storage::{
storage_map::StorageMap,
storage_vec::*,
},
};
use pyth_interface::{
data_structures::{
batch_attestation_update::*,
data_source::*,
governance_instruction::*,
governance_payload::*,
price::*,
update_type::UpdateType,
wormhole_light::*,
},
errors::{
PythError,
WormholeError,
},
events::{
ConstructedEvent,
ContractUpgradedEvent,
DataSourcesSetEvent,
FeeSetEvent,
GovernanceDataSourceSetEvent,
NewGuardianSetEvent,
UpdatedPriceFeedsEvent,
ValidPeriodSetEvent,
},
pyth_merkle_proof::validate_proof,
PythCore,
PythInfo,
PythInit,
utils::total_fee,
WormholeGuardians,
};
use sway_libs::ownership::*;
use standards::src5::{SRC5, State};
const GUARDIAN_SET_EXPIRATION_TIME_SECONDS: u64 = 86400; // 24 hours in seconds
configurable {
DEPLOYER: Identity = Identity::Address(Address::from(ZERO_B256)),
}
storage {
// | |
// --+-- PYTH STATE --+--
// | |
// (chainId, emitterAddress) => isValid; takes advantage of
// constant-time mapping lookup for VM verification
is_valid_data_source: StorageMap<DataSource, bool> = StorageMap {},
// Mapping of cached price information
// priceId => PriceInfo
latest_price_feed: StorageMap<PriceFeedId, PriceFeed> = StorageMap {},
// Fee required for each update
single_update_fee: u64 = 0,
// For tracking all active emitter/chain ID pairs
valid_data_sources: StorageVec<DataSource> = StorageVec {},
/// Maximum acceptable time period before price is considered to be stale.
/// This includes attestation delay, block time, and potential clock drift
/// between the source/target chains.
valid_time_period_seconds: u64 = 0,
/// Governance data source. VAA messages from this source can change this contract
/// state. e.g., upgrade the contract, change the valid data sources, and more.
governance_data_source: DataSource = DataSource {
chain_id: 0u16,
emitter_address: ZERO_B256,
},
/// Index of the governance data source, increased each time the governance data source changes.
governance_data_source_index: u32 = 0,
/// Sequence number of the last executed governance message. Any governance message
/// with a lower or equal sequence number will be discarded. This prevents double-execution,
/// and also makes sure that messages are executed in the right order.
last_executed_governance_sequence: u64 = 0,
/// Chain ID of the contract
chain_id: u16 = 0,
/// | |
/// --+-- WORMHOLE STATE --+--
/// | |
/// Mapping of consumed governance actions
wormhole_consumed_governance_actions: StorageMap<b256, bool> = StorageMap {},
/// Mapping of guardian_set_index => guardian set
wormhole_guardian_sets: StorageMap<u32, StorageGuardianSet> = StorageMap {},
/// Current active guardian set index
wormhole_guardian_set_index: u32 = 0,
/// Using Ethereum's Wormhole governance
wormhole_governance_data_source: DataSource = DataSource {
chain_id: 0u16,
emitter_address: ZERO_B256,
},
/// | |
/// --+-- GOVERNANCE STATE --+--
/// | |
current_implementation: Identity = Identity::Address(Address::from(ZERO_B256)),
}
impl SRC5 for Contract {
#[storage(read)]
fn owner() -> State {
_owner()
}
}
impl PythCore for Contract {
#[storage(read)]
fn ema_price(price_feed_id: PriceFeedId) -> Price {
ema_price_no_older_than(valid_time_period(), price_feed_id)
}
#[storage(read)]
fn ema_price_no_older_than(time_period: u64, price_feed_id: PriceFeedId) -> Price {
ema_price_no_older_than(time_period, price_feed_id)
}
#[storage(read)]
fn ema_price_unsafe(price_feed_id: PriceFeedId) -> Price {
ema_price_unsafe(price_feed_id)
}
#[storage(read), payable]
fn parse_price_feed_updates(
max_publish_time: u64,
min_publish_time: u64,
target_price_feed_ids: Vec<PriceFeedId>,
update_data: Vec<Bytes>,
) -> Vec<PriceFeed> {
require(
msg_asset_id() == AssetId::base(),
PythError::FeesCanOnlyBePaidInTheBaseAsset,
);
let required_fee = update_fee(update_data);
require(msg_amount() >= required_fee, PythError::InsufficientFee);
let mut output_price_feeds: Vec<PriceFeed> = Vec::with_capacity(target_price_feed_ids.len());
let mut i = 0;
while i < update_data.len() {
let data = update_data.get(i).unwrap();
match UpdateType::determine_type(data) {
UpdateType::Accumulator(accumulator_update) => {
let (mut offset, digest, number_of_updates, encoded) = accumulator_update.verify_and_parse(
current_guardian_set_index(),
storage
.wormhole_guardian_sets,
storage
.is_valid_data_source,
);
let mut i_2 = 0;
while i_2 < number_of_updates {
let (new_offset, price_feed) = PriceFeed::extract_from_merkle_proof(digest, encoded, offset);
offset = new_offset;
if price_feed.id.is_target(target_price_feed_ids) == false {
i_2 += 1;
continue;
}
if price_feed.price.publish_time >= min_publish_time && price_feed.price.publish_time <= max_publish_time {
// check if output_price_feeds already contains a PriceFeed with price_feed.id, if so continue as we only want 1
// output PriceFeed per target ID
if price_feed.id.is_contained_within(output_price_feeds) {
i_2 += 1;
continue;
}
output_price_feeds.push(price_feed)
}
i_2 += 1;
}
require(offset == encoded.len(), PythError::InvalidUpdateDataLength);
},
UpdateType::BatchAttestation(batch_attestation_update) => {
let vm = WormholeVM::parse_and_verify_pyth_vm(
current_guardian_set_index(),
batch_attestation_update
.data,
storage
.wormhole_guardian_sets,
storage
.is_valid_data_source,
);
let (mut attestation_index, number_of_attestations, attestation_size) = parse_and_verify_batch_attestation_header(vm.payload);
let attestation_size_u16 = attestation_size.as_u64();
let mut i_2: u16 = 0;
while i_2 < number_of_attestations {
let (_, slice) = vm.payload.split_at(attestation_index + 32);
let (price_feed_id, _) = slice.split_at(32);
let price_feed_id: PriceFeedId = price_feed_id.into();
if price_feed_id.is_target(target_price_feed_ids) == false {
attestation_index += attestation_size_u16;
i_2 += 1;
continue;
}
let price_feed = PriceFeed::parse_attestation(attestation_size, vm.payload, attestation_index);
if price_feed.price.publish_time >= min_publish_time && price_feed.price.publish_time <= max_publish_time {
// check if output_price_feeds already contains a PriceFeed with price_feed.id, if so continue;
// as we only want 1 output PriceFeed per target ID
if price_feed.id.is_contained_within(output_price_feeds) {
attestation_index += attestation_size_u16;
i_2 += 1;
continue;
}
output_price_feeds.push(price_feed)
}
attestation_index += attestation_size_u16;
i_2 += 1;
}
}
}
i += 1;
}
require(
target_price_feed_ids
.len() == output_price_feeds
.len(),
PythError::PriceFeedNotFoundWithinRange,
);
output_price_feeds
}
#[storage(read)]
fn price(price_feed_id: PriceFeedId) -> Price {
price_no_older_than(valid_time_period(), price_feed_id)
}
#[storage(read)]
fn price_no_older_than(time_period: u64, price_feed_id: PriceFeedId) -> Price {
price_no_older_than(time_period, price_feed_id)
}
#[storage(read)]
fn price_unsafe(price_feed_id: PriceFeedId) -> Price {
price_unsafe(price_feed_id)
}
#[storage(read)]
fn update_fee(update_data: Vec<Bytes>) -> u64 {
update_fee(update_data)
}
#[storage(read, write), payable]
fn update_price_feeds(update_data: Vec<Bytes>) {
update_price_feeds(update_data)
}
#[storage(read, write), payable]
fn update_price_feeds_if_necessary(
price_feed_ids: Vec<PriceFeedId>,
publish_times: Vec<u64>,
update_data: Vec<Bytes>,
) {
require(
price_feed_ids
.len() == publish_times
.len(),
PythError::LengthOfPriceFeedIdsAndPublishTimesMustMatch,
);
let mut i = 0;
while i < price_feed_ids.len() {
if latest_publish_time(price_feed_ids.get(i).unwrap()) < publish_times.get(i).unwrap()
{
update_price_feeds(update_data);
return;
}
i += 1;
}
}
#[storage(read)]
fn valid_time_period() -> u64 {
valid_time_period()
}
}
/// PythCore Private Functions ///
#[storage(read)]
fn ema_price_no_older_than(time_period: u64, price_feed_id: PriceFeedId) -> Price {
let price = ema_price_unsafe(price_feed_id);
let current_time = timestamp();
require(
current_time - price.publish_time <= time_period,
PythError::OutdatedPrice,
);
price
}
#[storage(read)]
fn ema_price_unsafe(price_feed_id: PriceFeedId) -> Price {
let price_feed = storage.latest_price_feed.get(price_feed_id).try_read();
require(price_feed.is_some(), PythError::PriceFeedNotFound);
price_feed.unwrap().ema_price
}
#[storage(read)]
fn price_no_older_than(time_period: u64, price_feed_id: PriceFeedId) -> Price {
let price = price_unsafe(price_feed_id);
let current_time = timestamp();
// Mimicking saturating subtraction to avoid underflow
let time_difference = if current_time > price.publish_time {
current_time - price.publish_time
} else {
0
};
require(time_difference <= time_period, PythError::OutdatedPrice);
price
}
#[storage(read)]
fn price_unsafe(price_feed_id: PriceFeedId) -> Price {
let price_feed = storage.latest_price_feed.get(price_feed_id).try_read();
require(price_feed.is_some(), PythError::PriceFeedNotFound);
price_feed.unwrap().price
}
#[storage(read)]
fn update_fee(update_data: Vec<Bytes>) -> u64 {
let mut total_number_of_updates = 0;
let mut i = 0;
while i < update_data.len() {
let data = update_data.get(i).unwrap();
match UpdateType::determine_type(data) {
UpdateType::Accumulator(accumulator_update) => {
let proof_size_offset = accumulator_update.verify();
total_number_of_updates += accumulator_update.total_updates(proof_size_offset);
},
UpdateType::BatchAttestation => {
total_number_of_updates += 1;
},
}
i += 1;
}
total_fee(total_number_of_updates, storage.single_update_fee)
}
#[storage(read, write), payable]
fn update_price_feeds(update_data: Vec<Bytes>) {
require(
msg_asset_id() == AssetId::base(),
PythError::FeesCanOnlyBePaidInTheBaseAsset,
);
let mut total_number_of_updates = 0;
// let mut updated_price_feeds: Vec<PriceFeedId> = Vec::new(); // TODO: requires append for Vec
let mut i = 0;
while i < update_data.len() {
let data = update_data.get(i).unwrap();
match UpdateType::determine_type(data) {
UpdateType::Accumulator(accumulator_update) => {
let (number_of_updates, _updated_ids) = accumulator_update.update_price_feeds(
current_guardian_set_index(),
storage
.wormhole_guardian_sets,
storage
.latest_price_feed,
storage
.is_valid_data_source,
);
// updated_price_feeds.append(updated_ids); // TODO: requires append for Vec
total_number_of_updates += number_of_updates;
},
UpdateType::BatchAttestation(batch_attestation_update) => {
let _updated_ids = batch_attestation_update.update_price_feeds(
current_guardian_set_index(),
storage
.wormhole_guardian_sets,
storage
.latest_price_feed,
storage
.is_valid_data_source,
);
// updated_price_feeds.append(updated_ids); // TODO: requires append for Vec
total_number_of_updates += 1;
},
}
i += 1;
}
let required_fee = total_fee(total_number_of_updates, storage.single_update_fee);
require(msg_amount() >= required_fee, PythError::InsufficientFee);
// log(UpdatedPriceFeedsEvent { // TODO: requires append for Vec
// updated_price_feeds,
// })
}
#[storage(read)]
fn valid_time_period() -> u64 {
storage.valid_time_period_seconds.read()
}
#[storage(read)]
fn governance_data_source() -> DataSource {
storage.governance_data_source.read()
}
#[storage(write)]
fn set_governance_data_source(data_source: DataSource) {
storage.governance_data_source.write(data_source);
}
#[storage(read)]
fn governance_data_source_index() -> u32 {
storage.governance_data_source_index.read()
}
#[storage(write)]
fn set_governance_data_source_index(index: u32) {
storage.governance_data_source_index.write(index);
}
#[storage(read)]
fn last_executed_governance_sequence() -> u64 {
storage.last_executed_governance_sequence.read()
}
#[storage(write)]
fn set_last_executed_governance_sequence(sequence: u64) {
storage.last_executed_governance_sequence.write(sequence);
}
#[storage(read)]
fn chain_id() -> u16 {
storage.chain_id.read()
}
#[storage(read)]
fn current_implementation() -> Identity {
storage.current_implementation.read()
}
impl PythInit for Contract {
#[storage(read, write)]
fn constructor(
data_sources: Vec<DataSource>,
governance_data_source: DataSource,
wormhole_governance_data_source: DataSource,
single_update_fee: u64,
valid_time_period_seconds: u64,
wormhole_guardian_set_addresses: Vec<b256>,
wormhole_guardian_set_index: u32,
chain_id: u16,
) {
// This function sets the passed identity as the initial owner. https://github.com/FuelLabs/sway-libs/blob/8045a19e3297599750abdf6300c11e9927a29d40/libs/src/ownership.sw#L127-L138
initialize_ownership(DEPLOYER);
// This function ensures that the sender is the owner. https://github.com/FuelLabs/sway-libs/blob/8045a19e3297599750abdf6300c11e9927a29d40/libs/src/ownership.sw#L59-L65
only_owner();
require(data_sources.len() > 0, PythError::InvalidDataSourcesLength);
let mut i = 0;
while i < data_sources.len() {
let data_source = data_sources.get(i).unwrap();
storage.is_valid_data_source.insert(data_source, true);
storage.valid_data_sources.push(data_source);
i += 1;
}
storage
.latest_price_feed
.write(StorageMap::<PriceFeedId, PriceFeed> {});
storage
.valid_time_period_seconds
.write(valid_time_period_seconds);
storage.single_update_fee.write(single_update_fee);
let guardian_length: u8 = wormhole_guardian_set_addresses.len().try_as_u8().unwrap();
let mut new_guardian_set = StorageGuardianSet::new(
0,
StorageKey::<StorageVec<b256>>::new(
sha256(("guardian_set_keys", wormhole_guardian_set_index)),
0,
ZERO_B256,
),
);
let mut i: u8 = 0;
while i < guardian_length {
let key: b256 = wormhole_guardian_set_addresses.get(i.as_u64()).unwrap();
new_guardian_set.keys.push(key);
i += 1;
}
storage
.wormhole_guardian_set_index
.write(wormhole_guardian_set_index);
storage
.wormhole_guardian_sets
.insert(wormhole_guardian_set_index, new_guardian_set);
storage.governance_data_source.write(governance_data_source);
storage
.wormhole_governance_data_source
.write(wormhole_governance_data_source);
storage.governance_data_source_index.write(0);
storage
.wormhole_consumed_governance_actions
.write(StorageMap::<b256, bool> {});
storage.chain_id.write(chain_id);
storage.last_executed_governance_sequence.write(0);
storage
.current_implementation
.write(Identity::Address(Address::from(ZERO_B256)));
// This function revokes ownership of the current owner and disallows any new owners. https://github.com/FuelLabs/sway-libs/blob/8045a19e3297599750abdf6300c11e9927a29d40/libs/src/ownership.sw#L89-L99
renounce_ownership();
log(ConstructedEvent {
guardian_set_index: wormhole_guardian_set_index,
})
}
}
impl PythInfo for Contract {
#[storage(read)]
fn valid_data_sources() -> Vec<DataSource> {
storage.valid_data_sources.load_vec()
}
#[storage(read)]
fn latest_publish_time(price_feed_id: PriceFeedId) -> u64 {
latest_publish_time(price_feed_id)
}
#[storage(read)]
fn price_feed_exists(price_feed_id: PriceFeedId) -> bool {
match storage.latest_price_feed.get(price_feed_id).try_read() {
Some(_) => true,
None => false,
}
}
#[storage(read)]
fn price_feed_unsafe(price_feed_id: PriceFeedId) -> PriceFeed {
let price_feed = storage.latest_price_feed.get(price_feed_id).try_read();
require(price_feed.is_some(), PythError::PriceFeedNotFound);
price_feed.unwrap()
}
#[storage(read)]
fn single_update_fee() -> u64 {
storage.single_update_fee.read()
}
#[storage(read)]
fn is_valid_data_source(data_source: DataSource) -> bool {
data_source.is_valid_data_source(storage.is_valid_data_source)
}
#[storage(read)]
fn last_executed_governance_sequence() -> u64 {
last_executed_governance_sequence()
}
#[storage(read)]
fn chain_id() -> u16 {
chain_id()
}
}
/// PythInfo Private Functions ///
#[storage(read)]
fn latest_publish_time(price_feed_id: PriceFeedId) -> u64 {
match storage.latest_price_feed.get(price_feed_id).try_read() {
Some(price_feed) => price_feed.price.publish_time,
None => 0,
}
}
impl WormholeGuardians for Contract {
#[storage(read)]
fn current_guardian_set_index() -> u32 {
current_guardian_set_index()
}
#[storage(read)]
fn current_wormhole_provider() -> DataSource {
current_wormhole_provider()
}
#[storage(read)]
fn guardian_set(index: u32) -> GuardianSet {
let stored_guardian_set = storage.wormhole_guardian_sets.get(index).try_read();
require(
stored_guardian_set
.is_some(),
PythError::GuardianSetNotFound,
);
GuardianSet::from_stored(stored_guardian_set.unwrap())
}
#[storage(read)]
fn governance_action_is_consumed(governance_action_hash: b256) -> bool {
governance_action_is_consumed(governance_action_hash)
}
#[storage(read, write)]
fn submit_new_guardian_set(encoded_vm: Bytes) {
submit_new_guardian_set(encoded_vm)
}
}
/// WormholeGuardians Private Functions ///
#[storage(read)]
fn current_guardian_set_index() -> u32 {
storage.wormhole_guardian_set_index.read()
}
#[storage(read)]
fn current_wormhole_provider() -> DataSource {
storage.wormhole_governance_data_source.read()
}
#[storage(read)]
fn governance_action_is_consumed(governance_action_hash: b256) -> bool {
match storage.wormhole_consumed_governance_actions.get(governance_action_hash).try_read() {
Some(bool_) => bool_,
None => false,
}
}
#[storage(read, write)]
fn submit_new_guardian_set(encoded_vm: Bytes) {
let vm: WormholeVM = WormholeVM::parse_and_verify_wormhole_vm(
current_guardian_set_index(),
encoded_vm,
storage
.wormhole_guardian_sets,
);
require(
vm.guardian_set_index == current_guardian_set_index(),
WormholeError::NotSignedByCurrentGuardianSet,
);
let current_wormhole_provider: DataSource = current_wormhole_provider();
require(
vm.emitter_chain_id == current_wormhole_provider
.chain_id,
WormholeError::InvalidGovernanceChain,
);
require(
vm.emitter_address == current_wormhole_provider
.emitter_address,
WormholeError::InvalidGovernanceContract,
);
require(
governance_action_is_consumed(vm.governance_action_hash) == false,
WormholeError::GovernanceActionAlreadyConsumed,
);
let current_guardian_set_index: u32 = current_guardian_set_index();
let upgrade: GuardianSetUpgrade = GuardianSetUpgrade::parse_encoded_upgrade(current_guardian_set_index, vm.payload);
storage
.wormhole_consumed_governance_actions
.insert(vm.governance_action_hash, true);
// Set expiry if current GuardianSet exists
let current_guardian_set = storage.wormhole_guardian_sets.get(current_guardian_set_index).try_read();
if current_guardian_set.is_some() {
let mut current_guardian_set = current_guardian_set.unwrap();
current_guardian_set.expiration_time = timestamp() + GUARDIAN_SET_EXPIRATION_TIME_SECONDS;
storage
.wormhole_guardian_sets
.insert(current_guardian_set_index, current_guardian_set);
}
storage
.wormhole_guardian_sets
.insert(upgrade.new_guardian_set_index, upgrade.new_guardian_set);
storage
.wormhole_guardian_set_index
.write(upgrade.new_guardian_set_index);
log(NewGuardianSetEvent {
governance_action_hash: vm.governance_action_hash,
new_guardian_set_index: upgrade.new_guardian_set_index,
})
}
/// Transfer the governance data source to a new value with sanity checks to ensure the new governance data source can manage the contract.
#[storage(read, write)]
fn authorize_governance_data_source_transfer(
payload: AuthorizeGovernanceDataSourceTransferPayload,
) {
let old_governance_data_source = governance_data_source();
// Parse and verify the VAA contained in the payload to ensure it's valid and can manage the contract
let vm: WormholeVM = WormholeVM::parse_and_verify_wormhole_vm(
current_guardian_set_index(),
payload
.claim_vaa,
storage
.wormhole_guardian_sets,
);
let gi = GovernanceInstruction::parse_governance_instruction(vm.payload);
require(
gi.target_chain_id == chain_id() || gi.target_chain_id == 0,
PythError::InvalidGovernanceTarget,
);
require(
match gi.action {
GovernanceAction::RequestGovernanceDataSourceTransfer => true,
_ => false,
},
PythError::InvalidGovernanceMessage,
);
let claim_payload = GovernanceInstruction::parse_request_governance_data_source_transfer_payload(gi.payload);
require(
governance_data_source_index() < claim_payload
.governance_data_source_index,
PythError::OldGovernanceMessage,
);
set_governance_data_source_index(claim_payload.governance_data_source_index);
let new_governance_data_source = DataSource {
chain_id: vm.emitter_chain_id,
emitter_address: vm.emitter_address,
};
set_governance_data_source(new_governance_data_source);
// Setting the last executed governance to the claimVaa sequence to avoid using older sequences.
set_last_executed_governance_sequence(vm.sequence);
log(GovernanceDataSourceSetEvent {
old_data_source: old_governance_data_source,
new_data_source: new_governance_data_source,
initial_sequence: vm.sequence,
});
}
#[storage(read, write)]
fn set_data_sources(payload: SetDataSourcesPayload) {
let old_data_sources = storage.valid_data_sources.load_vec();
let mut i = 0;
while i < old_data_sources.len() {
let data_source = old_data_sources.get(i).unwrap();
storage.is_valid_data_source.insert(data_source, false);
i += 1;
}
// Clear the current list of valid data sources
storage.valid_data_sources.clear();
i = 0;
// Add new data sources from the payload and mark them as valid
while i < payload.data_sources.len() {
let data_source = payload.data_sources.get(i).unwrap();
storage.valid_data_sources.push(data_source);
storage.is_valid_data_source.insert(data_source, true);
i += 1;
}
// Emit an event with the old and new data sources
log(DataSourcesSetEvent {
old_data_sources: old_data_sources,
new_data_sources: storage.valid_data_sources.load_vec(),
});
}
#[storage(read, write)]
fn set_fee(payload: SetFeePayload) {
let old_fee = storage.single_update_fee.read();
storage.single_update_fee.write(payload.new_fee);
log(FeeSetEvent {
old_fee,
new_fee: payload.new_fee,
});
}
#[storage(read, write)]
fn set_valid_period(payload: SetValidPeriodPayload) {
let old_valid_period = storage.valid_time_period_seconds.read();
storage
.valid_time_period_seconds
.write(payload.new_valid_period);
log(ValidPeriodSetEvent {
old_valid_period,
new_valid_period: payload.new_valid_period,
});
}
abi PythGovernance {
#[storage(read)]
fn governance_data_source() -> DataSource;
#[storage(read, write)]
fn execute_governance_instruction(encoded_vm: Bytes);
}
impl PythGovernance for Contract {
#[storage(read)]
fn governance_data_source() -> DataSource {
governance_data_source()
}
#[storage(read, write)]
fn execute_governance_instruction(encoded_vm: Bytes) {
execute_governance_instruction(encoded_vm)
}
}
#[storage(read, write)]
fn execute_governance_instruction(encoded_vm: Bytes) {
let vm = verify_governance_vm(encoded_vm);
// Log so that the WormholeVM struct will show up in the ABI and can be used in the tests
log(vm);
let gi = GovernanceInstruction::parse_governance_instruction(vm.payload);
// Log so that the GovernanceInstruction struct will show up in the ABI and can be used in the tests
log(gi);
require(
gi.target_chain_id == chain_id() || gi.target_chain_id == 0,
PythError::InvalidGovernanceTarget,
);
match gi.action {
GovernanceAction::UpgradeContract => {
require(gi.target_chain_id != 0, PythError::InvalidGovernanceTarget);
// TODO: implement upgrade_upgradeable_contract(uc) when Fuel releases the upgrade standard library;
log("Upgrade functionality not implemented");
revert(0u64);
},
GovernanceAction::AuthorizeGovernanceDataSourceTransfer => {
let agdst = GovernanceInstruction::parse_authorize_governance_data_source_transfer_payload(gi.payload);
log(agdst);
authorize_governance_data_source_transfer(agdst);
},
GovernanceAction::SetDataSources => {
let sdsp = GovernanceInstruction::parse_set_data_sources_payload(gi.payload);
log(sdsp);
set_data_sources(sdsp);
},
GovernanceAction::SetFee => {
let sf = GovernanceInstruction::parse_set_fee_payload(gi.payload);
log(sf);
set_fee(sf);
},
GovernanceAction::SetValidPeriod => {
let svp = GovernanceInstruction::parse_set_valid_period_payload(gi.payload);
log(svp);
set_valid_period(svp);
},
GovernanceAction::RequestGovernanceDataSourceTransfer => {
// RequestGovernanceDataSourceTransfer can be only part of AuthorizeGovernanceDataSourceTransfer message
// The `revert` function only accepts u64, so as
// a workaround we use require.
require(false, PythError::InvalidGovernanceMessage);
},
_ => {
// The `revert` function only accepts u64, so as
// a workaround we use require.
require(false, PythError::InvalidGovernanceMessage);
}
}
}
#[storage(read, write)]
fn verify_governance_vm(encoded_vm: Bytes) -> WormholeVM {
let vm: WormholeVM = WormholeVM::parse_and_verify_wormhole_vm(
current_guardian_set_index(),
encoded_vm,
storage
.wormhole_guardian_sets,
);
require(
storage
.governance_data_source
.read()
.is_valid_governance_data_source(vm.emitter_chain_id, vm.emitter_address),
PythError::InvalidGovernanceDataSource,
);
require(
vm.sequence > last_executed_governance_sequence(),
PythError::OldGovernanceMessage,
);
set_last_executed_governance_sequence(vm.sequence);
vm
}