[sui 3/x]: events, dynamic fields, `update_price_feed`, `PriceInfoObject` (#714)
* state getters and setters, change Move.toml dependency to sui/integration_v2 * finish state.move * add new line to pyth * use deployer cap pattern for state module * sui pyth * update price feeds, dynamic object fields, Sui object PriceInfoObject * register price info object with pyth state after creation * PriceFeedUpdateEvent
This commit is contained in:
parent
111b0fa09f
commit
e4db9cac81
|
@ -4,8 +4,8 @@ version = "0.0.1"
|
|||
|
||||
[dependencies.Sui]
|
||||
git = "https://github.com/MystenLabs/sui.git"
|
||||
subdir = "crates/sui-framework"
|
||||
rev = "157ac72030d014f17d76cefe81f3915b4afab2c9"
|
||||
subdir = "crates/sui-framework/packages/sui-framework"
|
||||
rev = "82c9c80c11488858f1d3930f47ec9f335a566683"
|
||||
|
||||
[dependencies.Wormhole]
|
||||
git = "https://github.com/wormhole-foundation/wormhole.git"
|
||||
|
|
|
@ -153,7 +153,7 @@ module pyth::batch_price_attestation {
|
|||
ema_timestamp = prev_publish_time;
|
||||
};
|
||||
|
||||
price_info::new(
|
||||
price_info::new_price_info(
|
||||
attestation_time,
|
||||
tx_context::epoch(ctx), //TODO - use Sui Clock to get timestamp in seconds
|
||||
price_feed::new(
|
||||
|
@ -172,7 +172,7 @@ module pyth::batch_price_attestation {
|
|||
|
||||
// A batch price attestation with a magic number of 0x50325749
|
||||
let bytes = x"5032574900030000000102000400951436e0be37536be96f0896366089506a59763d036728332d3e3038047851aea7c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d1000000000000049a0000000000000008fffffffb00000000000005dc0000000000000003000000000100000001000000006329c0eb000000006329c0e9000000006329c0e400000000000006150000000000000007215258d81468614f6b7e194c5d145609394f67b041e93e6695dcc616faadd0603b9551a68d01d954d6387aff4df1529027ffb2fee413082e509feb29cc4904fe000000000000041a0000000000000003fffffffb00000000000005cb0000000000000003010000000100000001000000006329c0eb000000006329c0e9000000006329c0e4000000000000048600000000000000078ac9cf3ab299af710d735163726fdae0db8465280502eb9f801f74b3c1bd190333832fad6e36eb05a8972fe5f219b27b5b2bb2230a79ce79beb4c5c5e7ecc76d00000000000003f20000000000000002fffffffb00000000000005e70000000000000003010000000100000001000000006329c0eb000000006329c0e9000000006329c0e40000000000000685000000000000000861db714e9ff987b6fedf00d01f9fea6db7c30632d6fc83b7bc9459d7192bc44a21a28b4c6619968bd8c20e95b0aaed7df2187fd310275347e0376a2cd7427db800000000000006cb0000000000000001fffffffb00000000000005e40000000000000003010000000100000001000000006329c0eb000000006329c0e9000000006329c0e400000000000007970000000000000001";
|
||||
destroy(deserialize(bytes, ctx(&mut test)));
|
||||
let _ = destroy(deserialize(bytes, ctx(&mut test)));
|
||||
test_scenario::end(test);
|
||||
}
|
||||
|
||||
|
@ -198,42 +198,38 @@ module pyth::batch_price_attestation {
|
|||
attestation_count: 4,
|
||||
attestation_size: 149,
|
||||
price_infos: vector<PriceInfo>[
|
||||
price_info::new(
|
||||
price_info::new_price_info(
|
||||
1663680747,
|
||||
arrival_time,
|
||||
price_feed::new(
|
||||
price_identifier::from_byte_vec(x"c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d1"),
|
||||
price::new(i64::new(1557, false), 7, i64::new(5, true), 1663680740),
|
||||
price::new(i64::new(1500, false), 3, i64::new(5, true), 1663680740),
|
||||
),
|
||||
),
|
||||
price_info::new(
|
||||
) ),
|
||||
price_info::new_price_info(
|
||||
1663680747,
|
||||
arrival_time,
|
||||
price_feed::new(
|
||||
price_identifier::from_byte_vec(x"3b9551a68d01d954d6387aff4df1529027ffb2fee413082e509feb29cc4904fe"),
|
||||
price::new(i64::new(1050, false), 3, i64::new(5, true), 1663680745),
|
||||
price::new(i64::new(1483, false), 3, i64::new(5, true), 1663680745),
|
||||
),
|
||||
),
|
||||
price_info::new(
|
||||
) ),
|
||||
price_info::new_price_info(
|
||||
1663680747,
|
||||
arrival_time,
|
||||
price_feed::new(
|
||||
price_identifier::from_byte_vec(x"33832fad6e36eb05a8972fe5f219b27b5b2bb2230a79ce79beb4c5c5e7ecc76d"),
|
||||
price::new(i64::new(1010, false), 2, i64::new(5, true), 1663680745),
|
||||
price::new(i64::new(1511, false), 3, i64::new(5, true), 1663680745),
|
||||
),
|
||||
),
|
||||
price_info::new(
|
||||
) ),
|
||||
price_info::new_price_info(
|
||||
1663680747,
|
||||
arrival_time,
|
||||
price_feed::new(
|
||||
price_identifier::from_byte_vec(x"21a28b4c6619968bd8c20e95b0aaed7df2187fd310275347e0376a2cd7427db8"),
|
||||
price::new(i64::new(1739, false), 1, i64::new(5, true), 1663680745),
|
||||
price::new(i64::new(1508, false), 3, i64::new(5, true), 1663680745),
|
||||
),
|
||||
),
|
||||
) ),
|
||||
],
|
||||
};
|
||||
|
||||
|
|
|
@ -1,11 +1,53 @@
|
|||
module pyth::data_source {
|
||||
use sui::dynamic_field::{Self};
|
||||
use sui::object::{UID};
|
||||
use sui::tx_context::{TxContext};
|
||||
|
||||
use pyth::set::{Self};
|
||||
|
||||
use wormhole::external_address::ExternalAddress;
|
||||
|
||||
const KEY: vector<u8> = b"data_sources";
|
||||
|
||||
struct DataSource has copy, drop, store {
|
||||
emitter_chain: u64,
|
||||
emitter_address: ExternalAddress,
|
||||
}
|
||||
|
||||
public fun new_data_source_registry(parent_id: &mut UID, ctx: &mut TxContext) {
|
||||
assert!(
|
||||
!dynamic_field::exists_(parent_id, KEY),
|
||||
0 // TODO - add custom error type
|
||||
);
|
||||
dynamic_field::add(
|
||||
parent_id,
|
||||
KEY,
|
||||
set::new<DataSource>(ctx)
|
||||
)
|
||||
}
|
||||
|
||||
public fun add(parent_id: &mut UID, data_source: DataSource) {
|
||||
assert!(
|
||||
!contains(parent_id, data_source),
|
||||
0 // TODO - add custom error message
|
||||
);
|
||||
set::add(
|
||||
dynamic_field::borrow_mut(parent_id, KEY),
|
||||
data_source
|
||||
)
|
||||
}
|
||||
|
||||
public fun empty(parent_id: &mut UID){
|
||||
set::empty<DataSource>(
|
||||
dynamic_field::borrow_mut(parent_id, KEY)
|
||||
)
|
||||
}
|
||||
|
||||
public fun contains(parent_id: &UID, data_source: DataSource): bool {
|
||||
let ref = dynamic_field::borrow(parent_id, KEY);
|
||||
set::contains<DataSource>(ref, data_source)
|
||||
}
|
||||
|
||||
public fun new(emitter_chain: u64, emitter_address: ExternalAddress): DataSource {
|
||||
DataSource {
|
||||
emitter_chain: emitter_chain,
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
module pyth::event {
|
||||
use sui::event::{Self};
|
||||
use pyth::price_feed::{PriceFeed};
|
||||
|
||||
friend pyth::pyth;
|
||||
friend pyth::state;
|
||||
|
||||
struct PythInitializationEvent has copy, drop {}
|
||||
|
||||
/// Signifies that a price feed has been updated
|
||||
struct PriceFeedUpdateEvent has copy, store, drop {
|
||||
/// Value of the price feed
|
||||
price_feed: PriceFeed,
|
||||
/// Timestamp of the update
|
||||
timestamp: u64,
|
||||
}
|
||||
|
||||
public(friend) fun emit_price_feed_update(price_feed: PriceFeed, timestamp: u64) {
|
||||
event::emit(
|
||||
PriceFeedUpdateEvent {
|
||||
price_feed,
|
||||
timestamp,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public(friend) fun emit_pyth_initialization_event() {
|
||||
event::emit(
|
||||
PythInitializationEvent {}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -23,8 +23,18 @@ module pyth::price_feed {
|
|||
}
|
||||
}
|
||||
|
||||
public fun get_price_identifier(price_feed: &PriceFeed): &PriceIdentifier {
|
||||
&price_feed.price_identifier
|
||||
public fun from(
|
||||
price_feed: &PriceFeed
|
||||
): PriceFeed {
|
||||
PriceFeed {
|
||||
price_identifier: price_feed.price_identifier,
|
||||
price: price_feed.price,
|
||||
ema_price: price_feed.ema_price,
|
||||
}
|
||||
}
|
||||
|
||||
public fun get_price_identifier(price_feed: &PriceFeed): PriceIdentifier {
|
||||
price_feed.price_identifier
|
||||
}
|
||||
|
||||
public fun get_price(price_feed: &PriceFeed): Price {
|
||||
|
|
|
@ -1,13 +1,76 @@
|
|||
module pyth::price_info {
|
||||
use pyth::price_feed::PriceFeed;
|
||||
use sui::object::{Self, UID, ID};
|
||||
use sui::tx_context::{TxContext};
|
||||
use sui::dynamic_object_field::{Self};
|
||||
use sui::table::{Self};
|
||||
|
||||
use pyth::price_feed::{Self, PriceFeed};
|
||||
use pyth::price_identifier::{PriceIdentifier};
|
||||
|
||||
const KEY: vector<u8> = b"price_info";
|
||||
|
||||
friend pyth::pyth;
|
||||
|
||||
/// Sui Object version of PriceInfo.
|
||||
/// Has a key and lives in global store.
|
||||
struct PriceInfoObject has key, store {
|
||||
id: UID,
|
||||
price_info: PriceInfo
|
||||
}
|
||||
|
||||
/// Copyable and droppable.
|
||||
struct PriceInfo has copy, drop, store {
|
||||
attestation_time: u64,
|
||||
arrival_time: u64,
|
||||
price_feed: PriceFeed,
|
||||
}
|
||||
|
||||
public fun new(attestation_time: u64, arrival_time: u64, price_feed: PriceFeed): PriceInfo {
|
||||
/// Creates a table which maps a PriceIdentifier to the
|
||||
/// UID (in bytes) of the corresponding Sui PriceInfoObject.
|
||||
public fun new_price_info_registry(parent_id: &mut UID, ctx: &mut TxContext) {
|
||||
assert!(
|
||||
!dynamic_object_field::exists_(parent_id, KEY),
|
||||
0 // TODO - add custom error message
|
||||
);
|
||||
dynamic_object_field::add(
|
||||
parent_id,
|
||||
KEY,
|
||||
table::new<PriceIdentifier, ID>(ctx)
|
||||
)
|
||||
}
|
||||
|
||||
public fun add(parent_id: &mut UID, price_identifier: PriceIdentifier, id: ID) {
|
||||
assert!(
|
||||
!contains(parent_id, price_identifier),
|
||||
0 // TODO - add custom error message
|
||||
);
|
||||
table::add(
|
||||
dynamic_object_field::borrow_mut(parent_id, KEY),
|
||||
price_identifier,
|
||||
id
|
||||
)
|
||||
}
|
||||
|
||||
public fun contains(parent_id: &UID, price_identifier: PriceIdentifier): bool {
|
||||
let ref = dynamic_object_field::borrow(parent_id, KEY);
|
||||
table::contains<PriceIdentifier, ID>(ref, price_identifier)
|
||||
}
|
||||
|
||||
public fun new_price_info_object(
|
||||
price_info: PriceInfo,
|
||||
ctx: &mut TxContext
|
||||
): PriceInfoObject {
|
||||
PriceInfoObject {
|
||||
id: object::new(ctx),
|
||||
price_info: price_info
|
||||
}
|
||||
}
|
||||
|
||||
public fun new_price_info(
|
||||
attestation_time: u64,
|
||||
arrival_time: u64,
|
||||
price_feed: PriceFeed,
|
||||
): PriceInfo {
|
||||
PriceInfo {
|
||||
attestation_time: attestation_time,
|
||||
arrival_time: arrival_time,
|
||||
|
@ -15,6 +78,27 @@ module pyth::price_info {
|
|||
}
|
||||
}
|
||||
|
||||
#[test_only]
|
||||
public fun destroy(price_info: PriceInfoObject){
|
||||
let PriceInfoObject {
|
||||
id: id,
|
||||
price_info: _,
|
||||
} = price_info;
|
||||
object::delete(id);
|
||||
}
|
||||
|
||||
public fun uid_to_inner(price_info: &PriceInfoObject): ID {
|
||||
object::uid_to_inner(&price_info.id)
|
||||
}
|
||||
|
||||
public fun get_price_info_from_price_info_object(price_info: &PriceInfoObject): PriceInfo {
|
||||
price_info.price_info
|
||||
}
|
||||
|
||||
public fun get_price_identifier(price_info: &PriceInfo): PriceIdentifier {
|
||||
price_feed::get_price_identifier(&price_info.price_feed)
|
||||
}
|
||||
|
||||
public fun get_price_feed(price_info: &PriceInfo): &PriceFeed {
|
||||
&price_info.price_feed
|
||||
}
|
||||
|
@ -26,4 +110,11 @@ module pyth::price_info {
|
|||
public fun get_arrival_time(price_info: &PriceInfo): u64 {
|
||||
price_info.arrival_time
|
||||
}
|
||||
|
||||
public(friend) fun update_price_info_object(
|
||||
price_info_object: &mut PriceInfoObject,
|
||||
price_info: PriceInfo
|
||||
) {
|
||||
price_info_object.price_info = price_info;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1,246 @@
|
|||
module pyth::pyth {}
|
||||
module pyth::pyth {
|
||||
use std::vector;
|
||||
use sui::tx_context::{TxContext};
|
||||
use sui::coin::{Coin};
|
||||
use sui::sui::{SUI};
|
||||
use sui::transfer::{Self};
|
||||
use sui::tx_context::{Self};
|
||||
|
||||
use pyth::event::{Self as pyth_event};
|
||||
use pyth::data_source::{Self, DataSource};
|
||||
use pyth::state::{Self as state, State as PythState, DeployerCap};
|
||||
use pyth::price_info::{Self, PriceInfo, PriceInfoObject};
|
||||
use pyth::batch_price_attestation::{Self};
|
||||
use pyth::price_feed::{Self};
|
||||
use pyth::price::{Self};
|
||||
|
||||
use wormhole::external_address::{Self};
|
||||
use wormhole::vaa::{Self};
|
||||
use wormhole::state::{State as WormState};
|
||||
|
||||
|
||||
/// Call init_and_share_state with deployer cap to initialize
|
||||
/// state and emit event corresponding to Pyth initialization.
|
||||
public entry fun init_pyth(
|
||||
deployer: DeployerCap,
|
||||
stale_price_threshold: u64,
|
||||
governance_emitter_chain_id: u64,
|
||||
governance_emitter_address: vector<u8>,
|
||||
data_sources_emitter_chain_ids: vector<u64>,
|
||||
data_sources_emitter_addresses: vector<vector<u8>>,
|
||||
update_fee: u64,
|
||||
ctx: &mut TxContext
|
||||
) {
|
||||
state::init_and_share_state(
|
||||
deployer,
|
||||
stale_price_threshold,
|
||||
update_fee,
|
||||
data_source::new(
|
||||
governance_emitter_chain_id,
|
||||
external_address::from_bytes(governance_emitter_address)),
|
||||
parse_data_sources(
|
||||
data_sources_emitter_chain_ids,
|
||||
data_sources_emitter_addresses,
|
||||
),
|
||||
ctx
|
||||
);
|
||||
|
||||
// Emit Pyth initialization event.
|
||||
pyth_event::emit_pyth_initialization_event();
|
||||
}
|
||||
|
||||
fun parse_data_sources(
|
||||
emitter_chain_ids: vector<u64>,
|
||||
emitter_addresses: vector<vector<u8>>
|
||||
): vector<DataSource> {
|
||||
|
||||
// TODO - add custom error type error::data_source_emitter_address_and_chain_ids_different_lengths()
|
||||
assert!(vector::length(&emitter_chain_ids) == vector::length(&emitter_addresses), 0);
|
||||
|
||||
let sources = vector::empty();
|
||||
let i = 0;
|
||||
while (i < vector::length(&emitter_chain_ids)) {
|
||||
vector::push_back(&mut sources, data_source::new(
|
||||
*vector::borrow(&emitter_chain_ids, i),
|
||||
external_address::from_bytes(*vector::borrow(&emitter_addresses, i))
|
||||
));
|
||||
|
||||
i = i + 1;
|
||||
};
|
||||
sources
|
||||
}
|
||||
|
||||
/// Create and share new price feed objects if they don't already exist.
|
||||
public fun create_price_feeds(
|
||||
worm_state: &WormState,
|
||||
pyth_state: &mut PythState,
|
||||
vaas: vector<vector<u8>>,
|
||||
ctx: &mut TxContext
|
||||
){
|
||||
while (!vector::is_empty(&vaas)) {
|
||||
let vaa = vector::pop_back(&mut vaas);
|
||||
|
||||
// Deserialize the VAA
|
||||
let vaa = vaa::parse_and_verify(worm_state, vaa, ctx);
|
||||
|
||||
// Check that the VAA is from a valid data source (emitter)
|
||||
assert!(
|
||||
state::is_valid_data_source(
|
||||
pyth_state,
|
||||
data_source::new(
|
||||
(vaa::emitter_chain(&vaa) as u64),
|
||||
vaa::emitter_address(&vaa))
|
||||
),
|
||||
0); // TODO - use custom error message - error::invalid_data_source()
|
||||
|
||||
// Deserialize the batch price attestation
|
||||
let price_infos = batch_price_attestation::destroy(batch_price_attestation::deserialize(vaa::take_payload(vaa), ctx));
|
||||
while (!vector::is_empty(&price_infos)){
|
||||
let cur_price_info = vector::pop_back(&mut price_infos);
|
||||
|
||||
// Only create new Sui PriceInfoObject if not already
|
||||
// registered with the Pyth State object.
|
||||
if (!state::price_feed_object_exists(
|
||||
pyth_state,
|
||||
price_feed::get_price_identifier(
|
||||
price_info::get_price_feed(&cur_price_info)
|
||||
)
|
||||
)
|
||||
){
|
||||
// Create and share newly created Sui PriceInfoObject containing a price feed,
|
||||
// and then register a copy of its ID with State.
|
||||
let new_price_info_object = price_info::new_price_info_object(cur_price_info, ctx);
|
||||
let price_identifier = price_info::get_price_identifier(&cur_price_info);
|
||||
let id = price_info::uid_to_inner(&new_price_info_object);
|
||||
|
||||
state::register_price_info_object(pyth_state, price_identifier, id);
|
||||
|
||||
transfer::public_share_object(new_price_info_object);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Update PriceInfo objects and corresponding price feeds with the
|
||||
/// data in the given VAAs.
|
||||
///
|
||||
/// The vaas argument is a vector of VAAs encoded as bytes.
|
||||
///
|
||||
/// The javascript https://github.com/pyth-network/pyth-js/tree/main/pyth-aptos-js package
|
||||
/// should be used to fetch these VAAs from the Price Service. More information about this
|
||||
/// process can be found at https://docs.pyth.network/consume-data.
|
||||
///
|
||||
/// The given fee must contain a sufficient number of coins to pay the update fee for the given vaas.
|
||||
/// The update fee amount can be queried by calling get_update_fee(&vaas).
|
||||
///
|
||||
/// Please read more information about the update fee here: https://docs.pyth.network/consume-data/on-demand#fees
|
||||
public fun update_price_feeds(
|
||||
worm_state: &WormState,
|
||||
pyth_state: &PythState,
|
||||
vaas: vector<vector<u8>>,
|
||||
price_info_objects: &mut vector<PriceInfoObject>,
|
||||
fee: Coin<SUI>,
|
||||
ctx: &mut TxContext
|
||||
) {
|
||||
// Charge the message update fee
|
||||
// TODO - error::insufficient_fee()
|
||||
//assert!(get_update_fee(&vaas) <= coin::value(&fee), 0);
|
||||
transfer::public_transfer(fee, @pyth);
|
||||
|
||||
// Update the price feed from each VAA
|
||||
while (!vector::is_empty(&vaas)) {
|
||||
update_price_feed_from_single_vaa(
|
||||
worm_state,
|
||||
pyth_state,
|
||||
vector::pop_back(&mut vaas),
|
||||
price_info_objects,
|
||||
ctx
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/// Precondition: A Sui object of type PriceInfoObject must exist for each update
|
||||
/// encoded in the worm_vaa (batch_attestation_vaa). These should be passed in
|
||||
/// via the price_info_objects argument.
|
||||
fun update_price_feed_from_single_vaa(
|
||||
worm_state: &WormState,
|
||||
pyth_state: &PythState,
|
||||
worm_vaa: vector<u8>,
|
||||
price_info_objects: &mut vector<PriceInfoObject>,
|
||||
ctx: &mut TxContext
|
||||
) {
|
||||
// Deserialize the VAA
|
||||
let vaa = vaa::parse_and_verify(worm_state, worm_vaa, ctx);
|
||||
|
||||
// Check that the VAA is from a valid data source (emitter)
|
||||
assert!(
|
||||
state::is_valid_data_source(
|
||||
pyth_state,
|
||||
data_source::new(
|
||||
(vaa::emitter_chain(&vaa) as u64),
|
||||
vaa::emitter_address(&vaa))
|
||||
),
|
||||
0); // TODO - use custom error message - error::invalid_data_source()
|
||||
|
||||
// Deserialize the batch price attestation
|
||||
let price_infos = batch_price_attestation::destroy(batch_price_attestation::deserialize(vaa::take_payload(vaa), ctx));
|
||||
|
||||
// Update price info objects.
|
||||
update_cache(price_infos, price_info_objects, ctx);
|
||||
}
|
||||
|
||||
/// Update PriceInfoObjects using up-to-date PriceInfos.
|
||||
fun update_cache(
|
||||
updates: vector<PriceInfo>,
|
||||
price_info_objects: &mut vector<PriceInfoObject>,
|
||||
ctx: &mut TxContext
|
||||
){
|
||||
while (!vector::is_empty(&updates)) {
|
||||
let update = vector::pop_back(&mut updates);
|
||||
let i = 0;
|
||||
let found = false;
|
||||
// Find PriceInfoObjects corresponding to the current update (PriceInfo).
|
||||
// TODO - This for loop might be expensive if there are a large
|
||||
// number of updates and/or price_info_objects we are updating.
|
||||
while (i < vector::length<PriceInfoObject>(price_info_objects)){
|
||||
// Check if the current price info object corresponds to the price feed that
|
||||
// the update is meant for.
|
||||
let price_info = price_info::get_price_info_from_price_info_object(vector::borrow(price_info_objects, i));
|
||||
if (price_info::get_price_identifier(&price_info) ==
|
||||
price_info::get_price_identifier(&update)){
|
||||
found = true;
|
||||
// TODO: use clock timestamp instead of epoch in the future
|
||||
pyth_event::emit_price_feed_update(price_feed::from(price_info::get_price_feed(&update)), tx_context::epoch(ctx));
|
||||
|
||||
// Update the price info object with the new updated price info.
|
||||
if (is_fresh_update(&update, vector::borrow(price_info_objects, i))){
|
||||
price_info::update_price_info_object(
|
||||
vector::borrow_mut(price_info_objects, i),
|
||||
update
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (!found){
|
||||
// TODO - throw error, since the price_feeds in price_info_objects do
|
||||
// not constitute a superset of the price_feeds to be updated
|
||||
}
|
||||
};
|
||||
vector::destroy_empty(updates);
|
||||
}
|
||||
|
||||
/// Determine if the given price update is "fresh": we have nothing newer already cached for that
|
||||
/// price feed within a PriceInfoObject.
|
||||
fun is_fresh_update(update: &PriceInfo, price_info_object: &PriceInfoObject): bool {
|
||||
// Get the timestamp of the update's current price
|
||||
let price_feed = price_info::get_price_feed(update);
|
||||
let update_timestamp = price::get_timestamp(&price_feed::get_price(price_feed));
|
||||
|
||||
// Get the timestamp of the cached data for the price identifier
|
||||
let cached_price_info = price_info::get_price_info_from_price_info_object(price_info_object);
|
||||
let cached_price_feed = price_info::get_price_feed(&cached_price_info);
|
||||
let cached_timestamp = price::get_timestamp(&price_feed::get_price(cached_price_feed));
|
||||
|
||||
update_timestamp > cached_timestamp
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
module pyth::state {
|
||||
use std::vector;
|
||||
use sui::object::{Self, UID};
|
||||
use sui::object::{Self, UID, ID};
|
||||
use sui::transfer::{Self};
|
||||
use sui::tx_context::{Self, TxContext};
|
||||
|
||||
use pyth::data_source::{DataSource};
|
||||
use pyth::set::{Self, Set};
|
||||
use pyth::data_source::{Self, DataSource};
|
||||
use pyth::price_info::{Self};
|
||||
use pyth::price_identifier::{PriceIdentifier};
|
||||
|
||||
friend pyth::pyth;
|
||||
|
||||
|
@ -17,19 +18,14 @@ module pyth::state {
|
|||
|
||||
struct State has key {
|
||||
id: UID,
|
||||
// TODO - Make data_sources a dynamic field of State,
|
||||
// inside of something embedded in State, because there will be
|
||||
// 10k+ data sources in the future, and we want to minimize the
|
||||
// size of State.
|
||||
data_sources: Set<DataSource>,
|
||||
governance_data_source: DataSource,
|
||||
last_executed_governance_sequence: u64,
|
||||
stale_price_threshold: u64,
|
||||
base_update_fee: u64
|
||||
base_update_fee: u64,
|
||||
}
|
||||
|
||||
fun init(ctx: &mut TxContext) {
|
||||
transfer::transfer(
|
||||
transfer::public_transfer(
|
||||
DeployerCap {
|
||||
id: object::new(ctx)
|
||||
},
|
||||
|
@ -49,20 +45,32 @@ module pyth::state {
|
|||
let DeployerCap { id } = deployer;
|
||||
object::delete(id);
|
||||
|
||||
// Convert the vector of DataSource objects into a set
|
||||
// of DataSource objects
|
||||
let data_sources = set::new<DataSource>(ctx);
|
||||
let uid = object::new(ctx);
|
||||
|
||||
// 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)) {
|
||||
set::add(&mut data_sources, vector::pop_back(&mut sources));
|
||||
data_source::add(&mut uid, vector::pop_back(&mut sources));
|
||||
};
|
||||
|
||||
// Share state so that is a shared Sui object.
|
||||
transfer::share_object(
|
||||
State {
|
||||
id: object::new(ctx),
|
||||
data_sources,
|
||||
id: uid,
|
||||
governance_data_source,
|
||||
last_executed_governance_sequence: 0,
|
||||
stale_price_threshold,
|
||||
base_update_fee
|
||||
base_update_fee,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -77,7 +85,7 @@ module pyth::state {
|
|||
}
|
||||
|
||||
public fun is_valid_data_source(s: &State, data_source: DataSource): bool {
|
||||
set::contains<DataSource>(&s.data_sources, data_source)
|
||||
data_source::contains(&s.id, data_source)
|
||||
}
|
||||
|
||||
public fun is_valid_governance_data_source(s: &State, source: DataSource): bool {
|
||||
|
@ -88,17 +96,24 @@ module pyth::state {
|
|||
s.last_executed_governance_sequence
|
||||
}
|
||||
|
||||
public fun price_feed_object_exists(s: &State, p: PriceIdentifier): bool {
|
||||
price_info::contains(&s.id, p)
|
||||
}
|
||||
|
||||
// Setters
|
||||
public(friend) fun set_data_sources(s: &mut State, new_sources: vector<DataSource>) {
|
||||
// Empty the existing set of data sources instead of dropping it,
|
||||
// because it does not have drop ability.
|
||||
set::empty<DataSource>(&mut s.data_sources);
|
||||
// Add new sources to state.data_sources.
|
||||
// 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)) {
|
||||
set::add(&mut s.data_sources, vector::pop_back(&mut 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_last_executed_governance_sequence(s: &mut State, sequence: u64) {
|
||||
s.last_executed_governance_sequence = sequence;
|
||||
}
|
||||
|
@ -114,4 +129,8 @@ module pyth::state {
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue