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

213 lines
6.8 KiB
Plaintext

module pyth::price_info {
use sui::object::{Self, UID, ID};
use sui::tx_context::{TxContext};
use sui::dynamic_object_field::{Self};
use sui::table::{Self};
use sui::coin::{Self, Coin};
use sui::sui::SUI;
use pyth::price_feed::{Self, PriceFeed};
use pyth::price_identifier::{PriceIdentifier};
const KEY: vector<u8> = b"price_info";
const FEE_STORAGE_KEY: vector<u8> = b"fee_storage";
const E_PRICE_INFO_REGISTRY_ALREADY_EXISTS: u64 = 0;
const E_PRICE_IDENTIFIER_ALREADY_REGISTERED: u64 = 1;
const E_PRICE_IDENTIFIER_NOT_REGISTERED: u64 = 2;
friend pyth::pyth;
friend pyth::state;
/// Sui object version of PriceInfo.
/// Has a key ability, is unique for each price identifier, 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,
}
/// Creates a table which maps a PriceIdentifier to the
/// UID (in bytes) of the corresponding Sui PriceInfoObject.
public(friend) fun new_price_info_registry(parent_id: &mut UID, ctx: &mut TxContext) {
assert!(
!dynamic_object_field::exists_(parent_id, KEY),
E_PRICE_INFO_REGISTRY_ALREADY_EXISTS
);
dynamic_object_field::add(
parent_id,
KEY,
table::new<PriceIdentifier, ID>(ctx)
)
}
public(friend) fun add(parent_id: &mut UID, price_identifier: PriceIdentifier, id: ID) {
assert!(
!contains(parent_id, price_identifier),
E_PRICE_IDENTIFIER_ALREADY_REGISTERED
);
table::add(
dynamic_object_field::borrow_mut(parent_id, KEY),
price_identifier,
id
)
}
/// Returns ID of price info object corresponding to price_identifier as a byte vector.
public fun get_id_bytes(parent_id: &UID, price_identifier: PriceIdentifier): vector<u8> {
assert!(
contains(parent_id, price_identifier),
E_PRICE_IDENTIFIER_NOT_REGISTERED
);
object::id_to_bytes(
table::borrow<PriceIdentifier, ID>(
dynamic_object_field::borrow(parent_id, KEY),
price_identifier
)
)
}
/// Returns ID of price info object corresponding to price_identifier as an ID.
public fun get_id(parent_id: &UID, price_identifier: PriceIdentifier): ID {
assert!(
contains(parent_id, price_identifier),
E_PRICE_IDENTIFIER_NOT_REGISTERED
);
object::id_from_bytes(
object::id_to_bytes(
table::borrow<PriceIdentifier, ID>(
dynamic_object_field::borrow(parent_id, KEY),
price_identifier
)
)
)
}
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 get_balance(price_info_object: &PriceInfoObject): u64 {
if (!dynamic_object_field::exists_with_type<vector<u8>, Coin<SUI>>(&price_info_object.id, FEE_STORAGE_KEY)) {
return 0
};
let fee = dynamic_object_field::borrow<vector<u8>, Coin<SUI>>(&price_info_object.id, FEE_STORAGE_KEY);
coin::value(fee)
}
public fun deposit_fee_coins(price_info_object: &mut PriceInfoObject, fee_coins: Coin<SUI>) {
if (!dynamic_object_field::exists_with_type<vector<u8>, Coin<SUI>>(&price_info_object.id, FEE_STORAGE_KEY)) {
dynamic_object_field::add(&mut price_info_object.id, FEE_STORAGE_KEY, fee_coins);
}
else {
let current_fee = dynamic_object_field::borrow_mut<vector<u8>, Coin<SUI>>(
&mut price_info_object.id,
FEE_STORAGE_KEY
);
coin::join(current_fee, fee_coins);
};
}
public(friend) fun new_price_info_object(
price_info: PriceInfo,
ctx: &mut TxContext
): PriceInfoObject {
PriceInfoObject {
id: object::new(ctx),
price_info
}
}
public fun new_price_info(
attestation_time: u64,
arrival_time: u64,
price_feed: PriceFeed,
): PriceInfo {
PriceInfo {
attestation_time,
arrival_time,
price_feed,
}
}
#[test]
public fun test_get_price_info_object_id_from_price_identifier(){
use sui::object::{Self};
use sui::test_scenario::{Self, ctx};
use pyth::price_identifier::{Self};
let scenario = test_scenario::begin(@pyth);
let uid = object::new(ctx(&mut scenario));
// Create a new price info object registry.
new_price_info_registry(&mut uid, ctx(&mut scenario));
// Register a price info object in the registry.
let price_identifier = price_identifier::from_byte_vec(x"ff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace");
// Create a new ID.
let id = object::id_from_bytes(x"19f253b07e88634bfd5a3a749f60bfdb83c9748910646803f06b60b76319e7ba");
add(&mut uid, price_identifier, id);
let result = get_id_bytes(&uid, price_identifier);
// Assert that ID matches original.
assert!(result==x"19f253b07e88634bfd5a3a749f60bfdb83c9748910646803f06b60b76319e7ba", 0);
// Clean up.
object::delete(uid);
test_scenario::end(scenario);
}
#[test_only]
public fun destroy(price_info: PriceInfoObject) {
let PriceInfoObject {
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
}
public fun get_attestation_time(price_info: &PriceInfo): u64 {
price_info.attestation_time
}
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 = new_price_info(
price_info.attestation_time,
price_info.arrival_time,
price_info.price_feed
);
}
}