Skip to content

feat(lazer/sui): initialize #2964

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lazer/contracts/sui/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
build/*
.trace
.coverage*
48 changes: 48 additions & 0 deletions lazer/contracts/sui/Move.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# @generated by Move, please check-in and do not edit manually.

[move]
version = 3
manifest_digest = "DD0B86B0E012F788977D2224EA46B39395FCF48AB7DAE200E70E6E12F9445868"
deps_digest = "F9B494B64F0615AED0E98FC12A85B85ECD2BC5185C22D30E7F67786BB52E507C"
dependencies = [
{ id = "Bridge", name = "Bridge" },
{ id = "MoveStdlib", name = "MoveStdlib" },
{ id = "Sui", name = "Sui" },
{ id = "SuiSystem", name = "SuiSystem" },
]

[[move.package]]
id = "Bridge"
source = { git = "https://github.com/MystenLabs/sui.git", rev = "b448b1d971bd6c1aac8ef4eee4305943806d5d5b", subdir = "crates/sui-framework/packages/bridge" }

dependencies = [
{ id = "MoveStdlib", name = "MoveStdlib" },
{ id = "Sui", name = "Sui" },
{ id = "SuiSystem", name = "SuiSystem" },
]

[[move.package]]
id = "MoveStdlib"
source = { git = "https://github.com/MystenLabs/sui.git", rev = "b448b1d971bd6c1aac8ef4eee4305943806d5d5b", subdir = "crates/sui-framework/packages/move-stdlib" }

[[move.package]]
id = "Sui"
source = { git = "https://github.com/MystenLabs/sui.git", rev = "b448b1d971bd6c1aac8ef4eee4305943806d5d5b", subdir = "crates/sui-framework/packages/sui-framework" }

dependencies = [
{ id = "MoveStdlib", name = "MoveStdlib" },
]

[[move.package]]
id = "SuiSystem"
source = { git = "https://github.com/MystenLabs/sui.git", rev = "b448b1d971bd6c1aac8ef4eee4305943806d5d5b", subdir = "crates/sui-framework/packages/sui-system" }

dependencies = [
{ id = "MoveStdlib", name = "MoveStdlib" },
{ id = "Sui", name = "Sui" },
]

[move.toolchain-version]
compiler-version = "1.53.2"
edition = "2024.beta"
flavor = "sui"
12 changes: 12 additions & 0 deletions lazer/contracts/sui/Move.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "pyth_lazer"
edition = "2024.beta"

[dependencies]

[addresses]
pyth_lazer = "0x0"

[dev-dependencies]

[dev-addresses]
25 changes: 25 additions & 0 deletions lazer/contracts/sui/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
## Pyth Lazer Sui Contract

`pyth_lazer` is a Sui package that allows consumers to easily parse and verify cryptographically signed price feed data from the Pyth Network's high-frequency Lazer protocol for use on-chain.

This package is built using the Move language and Sui framework.

### Build, test, deploy

Install Sui CLI and build the project:

```shell
brew install sui
sui move build
```

Run tests:

```shell
sui move test
sui move test test_parse_and_verify_le_ecdsa_update # run a specific test
```

Deploy:

TODO
69 changes: 69 additions & 0 deletions lazer/contracts/sui/sources/channel.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
module pyth_lazer::channel;

public enum Channel has copy, drop {
Invalid,
RealTime,
FixedRate50ms,
FixedRate200ms,
}

/// Create a new Invalid channel
public fun new_invalid(): Channel {
Channel::Invalid
}

/// Create a new RealTime channel
public fun new_real_time(): Channel {
Channel::RealTime
}

/// Create a new FixedRate50ms channel
public fun new_fixed_rate_50ms(): Channel {
Channel::FixedRate50ms
}

/// Create a new FixedRate200ms channel
public fun new_fixed_rate_200ms(): Channel {
Channel::FixedRate200ms
}

/// Check if the channel is Invalid
public fun is_invalid(channel: &Channel): bool {
match (channel) {
Channel::Invalid => true,
_ => false,
}
}

/// Check if the channel is RealTime
public fun is_real_time(channel: &Channel): bool {
match (channel) {
Channel::RealTime => true,
_ => false,
}
}

/// Check if the channel is FixedRate50ms
public fun is_fixed_rate_50ms(channel: &Channel): bool {
match (channel) {
Channel::FixedRate50ms => true,
_ => false,
}
}

/// Check if the channel is FixedRate200ms
public fun is_fixed_rate_200ms(channel: &Channel): bool {
match (channel) {
Channel::FixedRate200ms => true,
_ => false,
}
}

/// Get the update interval in milliseconds for fixed rate channels, returns 0 for non-fixed rate channels
public fun get_update_interval_ms(channel: &Channel): u64 {
match (channel) {
Channel::FixedRate50ms => 50,
Channel::FixedRate200ms => 200,
_ => 0,
}
}
147 changes: 147 additions & 0 deletions lazer/contracts/sui/sources/feed.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
module pyth_lazer::feed;

use pyth_lazer::i16::I16;
use pyth_lazer::i64::I64;

/// The feed struct is based on the Lazer rust protocol definition defined here:
/// https://github.com/pyth-network/pyth-crosschain/blob/main/lazer/sdk/rust/protocol/src/types.rs#L10
///
/// Some fields in Lazer are optional, as in Lazer might return None for them due to some conditions (for example,
/// not having enough publishers to calculate the price) and that is why they are represented as Option<Option<T>>.
/// The first Option<T> is for the existence of the field within the update data and the second Option<T> is for the
/// value of the field.
public struct Feed has copy, drop {
/// Unique identifier for the price feed (e.g., 1 for BTC/USD, 2 for ETH/USD)
feed_id: u32,
/// Current aggregate price from all publishers
price: Option<Option<I64>>,
/// Best bid price available across all publishers
best_bid_price: Option<Option<I64>>,
/// Best ask price available across all publishers
best_ask_price: Option<Option<I64>>,
/// Number of publishers contributing to this price feed
publisher_count: Option<u16>,
/// Price exponent (typically negative, e.g., -8 means divide price by 10^8)
exponent: Option<I16>,
/// Confidence interval representing price uncertainty
confidence: Option<Option<I64>>,
/// Funding rate for derivative products (e.g., perpetual futures)
funding_rate: Option<Option<I64>>,
/// Timestamp when the funding rate was last updated
funding_timestamp: Option<Option<u64>>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we just recently added funding_rate_interval.

}

/// Create a new Feed with the specified parameters
public fun new(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you need to make this public(crate) because you want when people get a Feed or Update object they be sure it's a valid Feed or Update.

feed_id: u32,
price: Option<Option<I64>>,
best_bid_price: Option<Option<I64>>,
best_ask_price: Option<Option<I64>>,
publisher_count: Option<u16>,
exponent: Option<I16>,
confidence: Option<Option<I64>>,
funding_rate: Option<Option<I64>>,
funding_timestamp: Option<Option<u64>>,
): Feed {
Feed {
feed_id,
price,
best_bid_price,
best_ask_price,
publisher_count,
exponent,
confidence,
funding_rate,
funding_timestamp,
}
}

/// Get the feed ID
public fun feed_id(feed: &Feed): u32 {
feed.feed_id
}

/// Get the price
public fun price(feed: &Feed): Option<Option<I64>> {
feed.price
}

/// Get the best bid price
public fun best_bid_price(feed: &Feed): Option<Option<I64>> {
feed.best_bid_price
}

/// Get the best ask price
public fun best_ask_price(feed: &Feed): Option<Option<I64>> {
feed.best_ask_price
}

/// Get the publisher count
public fun publisher_count(feed: &Feed): Option<u16> {
feed.publisher_count
}

/// Get the exponent
public fun exponent(feed: &Feed): Option<I16> {
feed.exponent
}

/// Get the confidence interval
public fun confidence(feed: &Feed): Option<Option<I64>> {
feed.confidence
}

/// Get the funding rate
public fun funding_rate(feed: &Feed): Option<Option<I64>> {
feed.funding_rate
}

/// Get the funding timestamp
public fun funding_timestamp(feed: &Feed): Option<Option<u64>> {
feed.funding_timestamp
}

/// Set the feed ID
public(package) fun set_feed_id(feed: &mut Feed, feed_id: u32) {
feed.feed_id = feed_id;
}

/// Set the price
public(package) fun set_price(feed: &mut Feed, price: Option<Option<I64>>) {
feed.price = price;
}

/// Set the best bid price
public(package) fun set_best_bid_price(feed: &mut Feed, best_bid_price: Option<Option<I64>>) {
feed.best_bid_price = best_bid_price;
}

/// Set the best ask price
public(package) fun set_best_ask_price(feed: &mut Feed, best_ask_price: Option<Option<I64>>) {
feed.best_ask_price = best_ask_price;
}

/// Set the publisher count
public(package) fun set_publisher_count(feed: &mut Feed, publisher_count: Option<u16>) {
feed.publisher_count = publisher_count;
}

/// Set the exponent
public(package) fun set_exponent(feed: &mut Feed, exponent: Option<I16>) {
feed.exponent = exponent;
}

/// Set the confidence interval
public(package) fun set_confidence(feed: &mut Feed, confidence: Option<Option<I64>>) {
feed.confidence = confidence;
}

/// Set the funding rate
public(package) fun set_funding_rate(feed: &mut Feed, funding_rate: Option<Option<I64>>) {
feed.funding_rate = funding_rate;
}

/// Set the funding timestamp
public(package) fun set_funding_timestamp(feed: &mut Feed, funding_timestamp: Option<Option<u64>>) {
feed.funding_timestamp = funding_timestamp;
}
Loading