-
Notifications
You must be signed in to change notification settings - Fork 16
feat(lazer): add sui example #59
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
Merged
Merged
Changes from 3 commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
name: Lazer Sui Move Build and Test | ||
|
||
on: | ||
push: | ||
branches: [ main ] | ||
pull_request: | ||
|
||
jobs: | ||
test: | ||
runs-on: ubuntu-latest | ||
defaults: | ||
run: | ||
working-directory: lazer/sui | ||
|
||
steps: | ||
- name: Checkout code | ||
uses: actions/checkout@v4 | ||
|
||
- name: Install Sui CLI | ||
run: | | ||
LATEST_RELEASE=$(curl -s https://api.github.com/repos/MystenLabs/sui/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') | ||
echo "Installing Sui CLI version: $LATEST_RELEASE" | ||
|
||
wget -q "https://github.com/MystenLabs/sui/releases/download/$LATEST_RELEASE/sui-$LATEST_RELEASE-ubuntu-x86_64.tgz" | ||
|
||
tar -xzf "sui-$LATEST_RELEASE-ubuntu-x86_64.tgz" | ||
chmod +x sui | ||
sudo mv sui /usr/local/bin/ | ||
|
||
sui --version | ||
|
||
- name: Build Sui Move contract | ||
run: sui move build | ||
|
||
- name: Run Sui Move tests | ||
run: sui move test | ||
|
||
- name: Test with verbose output | ||
run: sui move test --gas-limit 100000000 |
File renamed without changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 = "9A25A20E6E3BABDD1C296A4B0A45AE53C1ED219ECDBEDB8ABFE47CE82D7EB785" | ||
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 = "209f0da8e316", 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 = "209f0da8e316", subdir = "crates/sui-framework/packages/move-stdlib" } | ||
|
||
[[move.package]] | ||
id = "Sui" | ||
source = { git = "https://github.com/MystenLabs/sui.git", rev = "209f0da8e316", 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 = "209f0da8e316", subdir = "crates/sui-framework/packages/sui-system" } | ||
|
||
dependencies = [ | ||
{ id = "MoveStdlib", name = "MoveStdlib" }, | ||
{ id = "Sui", name = "Sui" }, | ||
] | ||
|
||
[move.toolchain-version] | ||
compiler-version = "1.52.2" | ||
edition = "2024.beta" | ||
flavor = "sui" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
[package] | ||
name = "lazer_example" | ||
edition = "2024.beta" | ||
|
||
[dependencies] | ||
|
||
[addresses] | ||
lazer_example = "0x0" | ||
|
||
[dev-dependencies] | ||
|
||
[dev-addresses] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# Pyth Lazer Sui Implementation | ||
|
||
**⚠️ DISCLAIMER: This is an example implementation for demonstration purposes only. It has not been audited and should be used at your own risk. Do not use this code in production without proper security review and testing.** | ||
|
||
A Sui Move implementation example for parsing and validating [Pyth Lazer](https://docs.pyth.network/lazer) price feed updates. This project demonstrates on-chain verification and parsing of cryptographically signed price feed data from the Pyth Network's high-frequency Lazer protocol. Look at the [`lazer_example` module](./sources/lazer_example.move) for the main implementation. | ||
|
||
## Prerequisites | ||
|
||
- [Sui CLI](https://docs.sui.io/guides/developer/getting-started/sui-install) installed | ||
- Basic familiarity with Move programming language | ||
|
||
## Building and Testing the Project | ||
|
||
1. **Build the project**: | ||
```bash | ||
sui move build | ||
``` | ||
|
||
2. **Run all tests**: | ||
```bash | ||
sui move test | ||
``` | ||
|
||
**Run specific test**: | ||
```bash | ||
sui move test test_parse_and_validate_update | ||
``` | ||
|
||
## Important Notes | ||
- The `parse_and_validate_update` function uses a single hardcoded public key for signature verification. However, in a real-world scenario, the set of valid public keys may change over time, and multiple keys might be required. For production use, store the authorized public keys in the contract's configuration storage and reference them dynamically, rather than relying on a hardcoded value. | ||
- There is no proper error handling in the `parse_and_validate_update` function and all the assertions use the same error code (0). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
/// Adopted from pyth::i64, adapted for i16 | ||
|
||
module lazer_example::i16; | ||
|
||
const MAX_POSITIVE_MAGNITUDE: u64 = (1 << 15) - 1; // 32767 | ||
const MAX_NEGATIVE_MAGNITUDE: u64 = (1 << 15); // 32768 | ||
|
||
/// To consume these values, first call `get_is_negative()` to determine if the I16 | ||
/// 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. | ||
public struct I16 has copy, drop, store { | ||
negative: bool, | ||
magnitude: u64, | ||
} | ||
|
||
public fun new(magnitude: u64, mut negative: bool): I16 { | ||
let mut 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; | ||
}; | ||
|
||
I16 { | ||
magnitude, | ||
negative, | ||
} | ||
} | ||
|
||
public fun get_is_negative(i: &I16): bool { | ||
i.negative | ||
} | ||
|
||
public fun get_magnitude_if_positive(in: &I16): u64 { | ||
assert!(!in.negative, 0); // error::negative_value() | ||
in.magnitude | ||
} | ||
|
||
public fun get_magnitude_if_negative(in: &I16): u64 { | ||
assert!(in.negative, 0); //error::positive_value() | ||
in.magnitude | ||
} | ||
|
||
public fun from_u16(from: u16): I16 { | ||
// Use the MSB to determine whether the number is negative or not. | ||
let from_u64 = (from as u64); | ||
let negative = (from_u64 >> 15) == 1; | ||
let magnitude = parse_magnitude(from_u64, negative); | ||
|
||
new(magnitude, negative) | ||
} | ||
|
||
fun parse_magnitude(from: u64, negative: bool): u64 { | ||
// If positive, then return the input verbatim | ||
if (!negative) { | ||
return from | ||
}; | ||
|
||
// Otherwise convert from two's complement by inverting and adding 1 | ||
// For 16-bit numbers, we only invert the lower 16 bits | ||
let inverted = from ^ 0xFFFF; | ||
inverted + 1 | ||
} | ||
|
||
#[test] | ||
fun test_max_positive_magnitude() { | ||
new(0x7FFF, false); // 32767 | ||
assert!(&new((1<<15) - 1, false) == &from_u16(((1<<15) - 1) as u16), 1); | ||
} | ||
|
||
#[test] | ||
#[expected_failure] | ||
fun test_magnitude_too_large_positive() { | ||
new(0x8000, false); // 32768 | ||
} | ||
|
||
#[test] | ||
fun test_max_negative_magnitude() { | ||
new(0x8000, true); // 32768 | ||
assert!(&new(1<<15, true) == &from_u16((1<<15) as u16), 1); | ||
} | ||
|
||
#[test] | ||
#[expected_failure] | ||
fun test_magnitude_too_large_negative() { | ||
new(0x8001, true); // 32769 | ||
} | ||
|
||
#[test] | ||
fun test_from_u16_positive() { | ||
assert!(from_u16(0x1234) == new(0x1234, false), 1); | ||
} | ||
|
||
#[test] | ||
fun test_from_u16_negative() { | ||
assert!(from_u16(0xEDCC) == new(0x1234, 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_u16(0), 1); | ||
assert!(&new(0, false) == &from_u16(0), 1); | ||
} | ||
|
||
#[test] | ||
fun test_boundary_values() { | ||
// Test positive boundary | ||
assert!(from_u16(0x7FFF) == new(32767, false), 1); | ||
|
||
// Test negative boundary | ||
assert!(from_u16(0x8000) == new(32768, true), 1); | ||
|
||
// Test -1 | ||
assert!(from_u16(0xFFFF) == new(1, true), 1); | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.