Forráskód Böngészése

sui: integration start, set up dependencies, copy over modules from Pyth-Aptos (#706)

* start

* move over initial contracts from aptos

* newline Move.toml
optke3 2 éve
szülő
commit
cbcc294017

+ 5 - 0
target_chains/sui/contracts/Makefile

@@ -0,0 +1,5 @@
+TARGETS := test
+
+.PHONY: test
+test:
+	sui move test

+ 17 - 0
target_chains/sui/contracts/Move.toml

@@ -0,0 +1,17 @@
+[package]
+name = "Pyth"
+version = "0.0.1"
+
+[dependencies.Sui]
+git = "https://github.com/MystenLabs/sui.git"
+subdir = "crates/sui-framework"
+rev = "157ac72030d014f17d76cefe81f3915b4afab2c9"
+
+[dependencies.Wormhole]
+git = "https://github.com/wormhole-foundation/wormhole.git"
+subdir = "sui/wormhole"
+rev = "sui/wormhole-cleanup"
+
+[addresses]
+pyth = "0x250"
+wormhole = "0x200"

+ 15 - 0
target_chains/sui/contracts/sources/data_source.move

@@ -0,0 +1,15 @@
+module pyth::data_source {
+    use wormhole::external_address::ExternalAddress;
+
+    struct DataSource has copy, drop, store {
+        emitter_chain: u64,
+        emitter_address: ExternalAddress,
+    }
+
+    public fun new(emitter_chain: u64, emitter_address: ExternalAddress): DataSource {
+        DataSource {
+            emitter_chain: emitter_chain,
+            emitter_address: emitter_address,
+        }
+    }
+}

+ 141 - 0
target_chains/sui/contracts/sources/deserialize.move

@@ -0,0 +1,141 @@
+ module pyth::deserialize {
+    use wormhole::bytes::{Self};
+    use wormhole::cursor::{take_rest, Cursor};
+    use pyth::i64::{Self, I64};
+
+    #[test_only]
+    use wormhole::cursor::{Self};
+
+    public fun deserialize_vector(cur: &mut Cursor<u8>, n: u64): vector<u8> {
+        bytes::take_bytes(cur, n)
+    }
+
+    public fun deserialize_u8(cur: &mut Cursor<u8>): u8 {
+        bytes::take_u8(cur)
+    }
+
+    public fun deserialize_u16(cur: &mut Cursor<u8>): u16 {
+        bytes::take_u16_be(cur)
+    }
+
+    public fun deserialize_u32(cur: &mut Cursor<u8>): u32 {
+        bytes::take_u32_be(cur)
+    }
+
+    public fun deserialize_i32(cur: &mut Cursor<u8>): I64 {
+        let deserialized = deserialize_u32(cur);
+        // If negative, pad the value
+        let negative = (deserialized >> 31) == 1;
+        if (negative) {
+            let padded = (0xFFFFFFFF << 32) + (deserialized as u64);
+            i64::from_u64((padded as u64))
+        } else {
+            i64::from_u64((deserialized as u64))
+        }
+    }
+
+    public fun deserialize_u64(cur: &mut Cursor<u8>): u64 {
+        bytes::take_u64_be(cur)
+    }
+
+    public fun deserialize_i64(cur: &mut Cursor<u8>): I64 {
+        i64::from_u64(deserialize_u64(cur))
+    }
+
+    #[test]
+    fun test_deserialize_u8() {
+        let input = x"48258963";
+        let cursor = cursor::new(input);
+
+        let result = deserialize_u8(&mut cursor);
+        assert!(result == 0x48, 1);
+
+        let rest = take_rest(cursor);
+        assert!(rest == x"258963", 1);
+    }
+
+    #[test]
+    fun test_deserialize_u16() {
+        let input = x"48258963";
+        let cursor = cursor::new(input);
+
+        let result = deserialize_u16(&mut cursor);
+        assert!(result == 0x4825, 1);
+
+        let rest = take_rest(cursor);
+        assert!(rest == x"8963", 1);
+    }
+
+    #[test]
+    fun test_deserialize_u32() {
+        let input = x"4825896349741695";
+        let cursor = cursor::new(input);
+
+        let result = deserialize_u32(&mut cursor);
+        assert!(result == 0x48258963, 1);
+
+        let rest = take_rest(cursor);
+        assert!(rest == x"49741695", 1);
+    }
+
+    #[test]
+    fun test_deserialize_i32_positive() {
+        let input = x"4825896349741695";
+        let cursor = cursor::new(input);
+
+        let result = deserialize_i32(&mut cursor);
+        assert!(result == i64::from_u64(0x48258963), 1);
+
+        let rest = take_rest(cursor);
+        assert!(rest == x"49741695", 1);
+    }
+
+    #[test]
+    fun test_deserialize_i32_negative() {
+        let input = x"FFFFFDC349741695";
+
+        let cursor = cursor::new(input);
+
+        let result = deserialize_i32(&mut cursor);
+        assert!(result == i64::from_u64(0xFFFFFFFFFFFFFDC3), 1);
+
+        let rest = take_rest(cursor);
+        assert!(rest == x"49741695", 1);
+    }
+
+    #[test]
+    fun test_deserialize_u64() {
+        let input = x"48258963497416957497253486";
+        let cursor = cursor::new(input);
+
+        let result = deserialize_u64(&mut cursor);
+        assert!(result == 0x4825896349741695, 1);
+
+        let rest = take_rest(cursor);
+        assert!(rest == x"7497253486", 1);
+    }
+
+    #[test]
+    fun test_deserialize_i64_positive() {
+        let input = x"48258963497416957497253486";
+        let cursor = cursor::new(input);
+
+        let result = deserialize_i64(&mut cursor);
+        assert!(result == i64::from_u64(0x4825896349741695), 1);
+
+        let rest = take_rest(cursor);
+        assert!(rest == x"7497253486", 1);
+    }
+
+    #[test]
+    fun test_deserialize_i64_negative() {
+        let input = x"FFFFFFFFFFFFFDC37497253486";
+        let cursor = cursor::new(input);
+
+        let result = deserialize_i64(&mut cursor);
+        assert!(result == i64::from_u64(0xFFFFFFFFFFFFFDC3), 1);
+
+        let rest = take_rest(cursor);
+        assert!(rest == x"7497253486", 1);
+    }
+}

+ 140 - 0
target_chains/sui/contracts/sources/i64.move

@@ -0,0 +1,140 @@
+module pyth::i64 {
+    //use pyth::error;
+
+    const MAX_POSITIVE_MAGNITUDE: u64 = (1 << 63) - 1;
+    const MAX_NEGATIVE_MAGNITUDE: u64 = (1 << 63);
+
+    /// As Move does not support negative numbers natively, we use our own internal
+    /// representation.
+    ///
+    /// To consume these values, first call `get_is_negative()` to determine if the I64
+    /// represents a negative or positive value. Then call `get_magnitude_if_positive()` or
+    /// `get_magnitude_if_negative()` to get the magnitude of the number in unsigned u64 format.
+    /// This API forces consumers to handle positive and negative numbers safely.
+    struct I64 has copy, drop, store {
+        negative: bool,
+        magnitude: u64,
+    }
+
+    public fun new(magnitude: u64, negative: bool): I64 {
+        let max_magnitude = MAX_POSITIVE_MAGNITUDE;
+        if (negative) {
+            max_magnitude = MAX_NEGATIVE_MAGNITUDE;
+        };
+        assert!(magnitude <= max_magnitude, 0); //error::magnitude_too_large()
+
+
+        // Ensure we have a single zero representation: (0, false).
+        // (0, true) is invalid.
+        if (magnitude == 0) {
+            negative = false;
+        };
+
+        I64 {
+            magnitude: magnitude,
+            negative: negative,
+        }
+    }
+
+    public fun get_is_negative(i: &I64): bool {
+        i.negative
+    }
+
+    public fun get_magnitude_if_positive(in: &I64): u64 {
+        assert!(!in.negative, 0); // error::negative_value()
+        in.magnitude
+    }
+
+    public fun get_magnitude_if_negative(in: &I64): u64 {
+        assert!(in.negative, 0); //error::positive_value()
+        in.magnitude
+    }
+
+    public fun from_u64(from: u64): I64 {
+        // Use the MSB to determine whether the number is negative or not.
+        let negative = (from >> 63) == 1;
+        let magnitude = parse_magnitude(from, negative);
+
+        new(magnitude, negative)
+    }
+
+    fun parse_magnitude(from: u64, negative: bool): u64 {
+        // If positive, then return the input verbatamin
+        if (!negative) {
+            return from
+        };
+
+        // Otherwise convert from two's complement by inverting and adding 1
+        let inverted = from ^ 0xFFFFFFFFFFFFFFFF;
+        inverted + 1
+    }
+
+    #[test]
+    fun test_max_positive_magnitude() {
+        new(0x7FFFFFFFFFFFFFFF, false);
+        assert!(&new(1<<63 - 1, false) == &from_u64(1<<63 - 1), 1);
+    }
+
+    #[test]
+    #[expected_failure]
+    fun test_magnitude_too_large_positive() {
+        new(0x8000000000000000, false);
+    }
+
+    #[test]
+    fun test_max_negative_magnitude() {
+        new(0x8000000000000000, true);
+        assert!(&new(1<<63, true) == &from_u64(1<<63), 1);
+    }
+
+    #[test]
+    #[expected_failure]
+    fun test_magnitude_too_large_negative() {
+        new(0x8000000000000001, true);
+    }
+
+    #[test]
+    fun test_from_u64_positive() {
+        assert!(from_u64(0x64673) == new(0x64673, false), 1);
+    }
+
+    #[test]
+    fun test_from_u64_negative() {
+        assert!(from_u64(0xFFFFFFFFFFFEDC73) == new(0x1238D, true), 1);
+    }
+
+    #[test]
+    fun test_get_is_negative() {
+        assert!(get_is_negative(&new(234, true)) == true, 1);
+        assert!(get_is_negative(&new(767, false)) == false, 1);
+    }
+
+    #[test]
+    fun test_get_magnitude_if_positive_positive() {
+        assert!(get_magnitude_if_positive(&new(7686, false)) == 7686, 1);
+    }
+
+    #[test]
+    #[expected_failure]
+    fun test_get_magnitude_if_positive_negative() {
+        assert!(get_magnitude_if_positive(&new(7686, true)) == 7686, 1);
+    }
+
+    #[test]
+    fun test_get_magnitude_if_negative_negative() {
+        assert!(get_magnitude_if_negative(&new(7686, true)) == 7686, 1);
+    }
+
+    #[test]
+    #[expected_failure]
+    fun test_get_magnitude_if_negative_positive() {
+        assert!(get_magnitude_if_negative(&new(7686, false)) == 7686, 1);
+    }
+
+    #[test]
+    fun test_single_zero_representation() {
+        assert!(&new(0, true) == &new(0, false), 1);
+        assert!(&new(0, true) == &from_u64(0), 1);
+        assert!(&new(0, false) == &from_u64(0), 1);
+    }
+}

+ 46 - 0
target_chains/sui/contracts/sources/price.move

@@ -0,0 +1,46 @@
+module pyth::price {
+    use pyth::i64::I64;
+
+    /// A price with a degree of uncertainty, represented as a price +- a confidence interval.
+    ///
+    /// The confidence interval roughly corresponds to the standard error of a normal distribution.
+    /// Both the price and confidence are stored in a fixed-point numeric representation,
+    /// `x * (10^expo)`, where `expo` is the exponent.
+    //
+    /// Please refer to the documentation at https://docs.pyth.network/consumers/best-practices for how
+    /// to how this price safely.
+    struct Price has copy, drop, store {
+        price: I64,
+        /// Confidence interval around the price
+        conf: u64,
+        /// The exponent
+        expo: I64,
+        /// Unix timestamp of when this price was computed
+        timestamp: u64,
+    }
+
+    public fun new(price: I64, conf: u64, expo: I64, timestamp: u64): Price {
+        Price {
+            price: price,
+            conf: conf,
+            expo: expo,
+            timestamp: timestamp,
+        }
+    }
+
+    public fun get_price(price: &Price): I64 {
+        price.price
+    }
+
+    public fun get_conf(price: &Price): u64 {
+        price.conf
+    }
+
+    public fun get_timestamp(price: &Price): u64 {
+        price.timestamp
+    }
+
+    public fun get_expo(price: &Price): I64 {
+        price.expo
+    }
+}

+ 37 - 0
target_chains/sui/contracts/sources/price_feed.move

@@ -0,0 +1,37 @@
+module pyth::price_feed {
+    use pyth::price_identifier::PriceIdentifier;
+    use pyth::price::Price;
+
+    /// PriceFeed represents a current aggregate price for a particular product.
+    struct PriceFeed has copy, drop, store {
+        /// The price identifier
+        price_identifier: PriceIdentifier,
+        /// The current aggregate price
+        price: Price,
+        /// The current exponentially moving average aggregate price
+        ema_price: Price,
+    }
+
+    public fun new(
+        price_identifier: PriceIdentifier,
+        price: Price,
+        ema_price: Price): PriceFeed {
+        PriceFeed {
+            price_identifier: price_identifier,
+            price: price,
+            ema_price: ema_price,
+        }
+    }
+
+    public fun get_price_identifier(price_feed: &PriceFeed): &PriceIdentifier {
+        &price_feed.price_identifier
+    }
+
+    public fun get_price(price_feed: &PriceFeed): Price {
+        price_feed.price
+    }
+
+    public fun get_ema_price(price_feed: &PriceFeed): Price {
+        price_feed.ema_price
+    }
+}

+ 22 - 0
target_chains/sui/contracts/sources/price_identifier.move

@@ -0,0 +1,22 @@
+module pyth::price_identifier {
+    use std::vector;
+    //use pyth::error;
+
+    const IDENTIFIER_BYTES_LENGTH: u64 = 32;
+
+    struct PriceIdentifier has copy, drop, store {
+        bytes: vector<u8>,
+    }
+
+    public fun from_byte_vec(bytes: vector<u8>): PriceIdentifier {
+        assert!(vector::length(&bytes) == IDENTIFIER_BYTES_LENGTH, 0); //error::incorrect_identifier_length()
+
+        PriceIdentifier {
+            bytes: bytes
+        }
+    }
+
+    public fun get_bytes(price_identifier: &PriceIdentifier): vector<u8> {
+        price_identifier.bytes
+    }
+}

+ 29 - 0
target_chains/sui/contracts/sources/price_info.move

@@ -0,0 +1,29 @@
+module pyth::price_info {
+    use pyth::price_feed::PriceFeed;
+
+    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 {
+        PriceInfo {
+            attestation_time: attestation_time,
+            arrival_time: arrival_time,
+            price_feed: 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
+    }
+}

+ 53 - 0
target_chains/sui/contracts/sources/price_status.move

@@ -0,0 +1,53 @@
+module pyth::price_status {
+    //use pyth::error;
+
+    /// The price feed is not currently updating for an unknown reason.
+    const UNKNOWN: u64 = 0;
+    /// The price feed is updating as expected.
+    const TRADING: u64 = 1;
+
+    /// PriceStatus represents the availability status of a price feed.
+    /// Prices should only be used if they have a status of trading.
+    struct PriceStatus has copy, drop, store {
+        status: u64,
+    }
+
+    public fun from_u64(status: u64): PriceStatus {
+        assert!(status <= TRADING, 0); // error::invalid_price_status()
+        PriceStatus {
+            status: status
+        }
+    }
+
+    public fun get_status(price_status: &PriceStatus): u64 {
+        price_status.status
+    }
+
+    public fun new_unknown(): PriceStatus {
+        PriceStatus {
+            status: UNKNOWN,
+        }
+    }
+
+    public fun new_trading(): PriceStatus {
+        PriceStatus {
+            status: TRADING,
+        }
+    }
+
+    #[test]
+    fun test_unknown_status() {
+        assert!(PriceStatus{ status: UNKNOWN } == from_u64(0), 1);
+    }
+
+    #[test]
+    fun test_trading_status() {
+        assert!(PriceStatus{ status: TRADING } == from_u64(1), 1);
+    }
+
+    #[test]
+    #[expected_failure]
+    fun test_invalid_price_status() {
+        from_u64(3);
+    }
+}