pyth-crosschain/target_chains/sui/contracts/sources/state.move

333 lines
11 KiB
Plaintext

module pyth::state {
use std::vector;
use sui::object::{Self, UID, ID};
use sui::transfer::{Self};
use sui::tx_context::{Self, TxContext};
use sui::dynamic_field::{Self as field};
use sui::package::{Self, UpgradeCap, UpgradeReceipt, UpgradeTicket};
use sui::balance::{Balance};
use sui::sui::SUI;
use pyth::data_source::{Self, DataSource};
use pyth::price_info::{Self};
use pyth::price_identifier::{PriceIdentifier};
use pyth::required_version::{Self, RequiredVersion};
use pyth::version_control::{Self as control};
use wormhole::setup::{assert_package_upgrade_cap};
use wormhole::consumed_vaas::{Self, ConsumedVAAs};
use wormhole::bytes32::{Self, Bytes32};
use wormhole::fee_collector::{Self, FeeCollector};
use wormhole::setup::{assert_package_upgrade_cap};
friend pyth::pyth;
friend pyth::pyth_tests;
friend pyth::governance_action;
friend pyth::set_update_fee;
friend pyth::set_stale_price_threshold;
friend pyth::set_data_sources;
friend pyth::governance;
friend pyth::set_governance_data_source;
friend pyth::migrate;
friend pyth::contract_upgrade;
friend pyth::transfer_fee;
const E_BUILD_VERSION_MISMATCH: u64 = 0;
const E_INVALID_BUILD_VERSION: u64 = 1;
const E_VAA_ALREADY_CONSUMED: u64 = 2;
/// Capability for creating a bridge state object, granted to sender when this
/// module is deployed
struct DeployerCap has key, store {
id: UID
}
/// Used as key for dynamic field reflecting whether `migrate` can be
/// called.
///
/// See `migrate` module for more info.
struct MigrationControl has store, drop, copy {}
struct State has key {
id: UID,
governance_data_source: DataSource,
stale_price_threshold: u64,
base_update_fee: u64,
consumed_vaas: ConsumedVAAs,
// Upgrade capability.
upgrade_cap: UpgradeCap,
// Fee collector.
fee_collector: FeeCollector,
/// Contract build version tracker.
required_version: RequiredVersion
}
fun init(ctx: &mut TxContext) {
transfer::public_transfer(
DeployerCap {
id: object::new(ctx)
},
tx_context::sender(ctx)
);
}
#[test_only]
public fun init_test_only(ctx: &mut TxContext) {
init(ctx);
// This will be created and sent to the transaction sender
// automatically when the contract is published.
transfer::public_transfer(
sui::package::test_publish(object::id_from_address(@pyth), ctx),
tx_context::sender(ctx)
);
}
/// Initialization
public(friend) fun init_and_share_state(
deployer: DeployerCap,
upgrade_cap: UpgradeCap,
stale_price_threshold: u64,
base_update_fee: u64,
governance_data_source: DataSource,
sources: vector<DataSource>,
ctx: &mut TxContext
) {
// Only init and share state once (in the initial deployment).
let version = wormhole::version_control::version();
assert!(version == 1, E_INVALID_BUILD_VERSION);
let DeployerCap { id } = deployer;
object::delete(id);
assert_package_upgrade_cap<DeployerCap>(
&upgrade_cap,
package::compatible_policy(),
1 // version
);
let uid = object::new(ctx);
field::add(&mut uid, MigrationControl {}, false);
// Create a set that contains all registered data sources and
// attach it to uid as a dynamic field to minimize the
// size of State.
data_source::new_data_source_registry(&mut uid, ctx);
// Create a table that tracks the object IDs of price feeds and
// attach it to the uid as a dynamic object field to minimize the
// size of State.
price_info::new_price_info_registry(&mut uid, ctx);
// Iterate through data sources and add them to the data source
// registry in state.
while (!vector::is_empty(&sources)) {
data_source::add(&mut uid, vector::pop_back(&mut sources));
};
let consumed_vaas = consumed_vaas::new(ctx);
let required_version = required_version::new(control::version(), ctx);
required_version::add<control::SetDataSources>(&mut required_version);
required_version::add<control::SetGovernanceDataSource>(&mut required_version);
required_version::add<control::SetStalePriceThreshold>(&mut required_version);
required_version::add<control::SetUpdateFee>(&mut required_version);
required_version::add<control::TransferFee>(&mut required_version);
required_version::add<control::UpdatePriceFeeds>(&mut required_version);
required_version::add<control::CreatePriceFeeds>(&mut required_version);
// Share state so that is a shared Sui object.
transfer::share_object(
State {
id: uid,
upgrade_cap,
governance_data_source,
stale_price_threshold,
base_update_fee,
consumed_vaas,
fee_collector: fee_collector::new(base_update_fee),
required_version
}
);
}
/// Retrieve current build version of latest upgrade.
public fun current_version(self: &State): u64 {
required_version::current(&self.required_version)
}
/// Issue an `UpgradeTicket` for the upgrade.
public(friend) fun authorize_upgrade(
self: &mut State,
implementation_digest: Bytes32
): UpgradeTicket {
let policy = package::upgrade_policy(&self.upgrade_cap);
// TODO: grab package ID from `UpgradeCap` and store it
// in a dynamic field. This will be the old ID after the upgrade.
// Both IDs will be emitted in a `ContractUpgraded` event.
package::authorize_upgrade(
&mut self.upgrade_cap,
policy,
bytes32::to_bytes(implementation_digest),
)
}
/// Finalize the upgrade that ran to produce the given `receipt`.
public(friend) fun commit_upgrade(
self: &mut State,
receipt: UpgradeReceipt
): ID {
// Uptick the upgrade cap version number using this receipt.
package::commit_upgrade(&mut self.upgrade_cap, receipt);
// Check that the upticked hard-coded version version agrees with the
// upticked version number.
assert!(
package::version(&self.upgrade_cap) == control::version() + 1,
E_BUILD_VERSION_MISMATCH
);
// Update global version.
required_version::update_latest(
&mut self.required_version,
&self.upgrade_cap
);
// Enable `migrate` to be called after commiting the upgrade.
//
// A separate method is required because `state` is a dependency of
// `migrate`. This method warehouses state modifications required
// for the new implementation plus enabling any methods required to be
// gated by the current implementation version. In most cases `migrate`
// is a no-op. But it still must be called in order to reset the
// migration control to `false`.
//
// See `migrate` module for more info.
enable_migration(self);
package::upgrade_package(&self.upgrade_cap)
}
/// Enforce a particular method to use the current build version as its
/// minimum required version. This method ensures that a method is not
/// backwards compatible with older builds.
public(friend) fun require_current_version<ControlType>(self: &mut State) {
required_version::require_current_version<ControlType>(
&mut self.required_version,
)
}
/// Check whether a particular method meets the minimum build version for
/// the latest Wormhole implementation.
public(friend) fun check_minimum_requirement<ControlType>(self: &State) {
required_version::check_minimum_requirement<ControlType>(
&self.required_version,
control::version()
)
}
// Upgrade and migration-related functionality
/// Check whether `migrate` can be called.
///
/// See `wormhole::migrate` module for more info.
public fun can_migrate(self: &State): bool {
*field::borrow(&self.id, MigrationControl {})
}
/// Allow `migrate` to be called after upgrade.
///
/// See `wormhole::migrate` module for more info.
public(friend) fun enable_migration(self: &mut State) {
*field::borrow_mut(&mut self.id, MigrationControl {}) = true;
}
/// Disallow `migrate` to be called.
///
/// See `wormhole::migrate` module for more info.
public(friend) fun disable_migration(self: &mut State) {
*field::borrow_mut(&mut self.id, MigrationControl {}) = false;
}
// Accessors
public fun get_stale_price_threshold_secs(s: &State): u64 {
s.stale_price_threshold
}
public fun get_base_update_fee(s: &State): u64 {
s.base_update_fee
}
public fun is_valid_data_source(s: &State, data_source: DataSource): bool {
data_source::contains(&s.id, data_source)
}
public fun is_valid_governance_data_source(s: &State, source: DataSource): bool {
s.governance_data_source == source
}
public fun price_feed_object_exists(s: &State, p: PriceIdentifier): bool {
price_info::contains(&s.id, p)
}
// Mutators and Setters
/// Withdraw collected fees when governance action to transfer fees to a
/// particular recipient.
///
/// See `pyth::transfer_fee` for more info.
public(friend) fun withdraw_fee(
self: &mut State,
amount: u64
): Balance<SUI> {
fee_collector::withdraw_balance(&mut self.fee_collector, amount)
}
public(friend) fun deposit_fee(self: &mut State, fee: Balance<SUI>) {
fee_collector::deposit_balance(&mut self.fee_collector, fee);
}
public(friend) fun set_fee_collector_fee(self: &mut State, amount: u64) {
fee_collector::change_fee(&mut self.fee_collector, amount);
}
public(friend) fun consume_vaa(state: &mut State, vaa_digest: Bytes32){
consumed_vaas::consume(&mut state.consumed_vaas, vaa_digest);
}
public(friend) fun set_data_sources(s: &mut State, new_sources: vector<DataSource>) {
// Empty the existing table of data sources registered in state.
data_source::empty(&mut s.id);
// Add the new data sources to the dynamic field registry.
while (!vector::is_empty(&new_sources)) {
data_source::add(&mut s.id, vector::pop_back(&mut new_sources));
};
}
public(friend) fun register_price_info_object(s: &mut State, price_identifier: PriceIdentifier, id: ID) {
price_info::add(&mut s.id, price_identifier, id);
}
public(friend) fun set_governance_data_source(s: &mut State, source: DataSource) {
s. governance_data_source = source;
}
public(friend) fun set_base_update_fee(s: &mut State, fee: u64) {
s.base_update_fee = fee;
}
public(friend) fun set_stale_price_threshold_secs(s: &mut State, threshold_secs: u64) {
s.stale_price_threshold = threshold_secs;
}
public(friend) fun register_price_feed(s: &mut State, p: PriceIdentifier, id: ID){
price_info::add(&mut s.id, p, id);
}
}