Skip to content

feat(pyth-lazer-publisher-sdk): Add SDK for Publisher Transactions #2557

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 26 commits into from
Apr 11, 2025
Merged
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
18 changes: 18 additions & 0 deletions .github/workflows/publish-rust-lazer-publisher-sdk.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Publish Rust package pyth-lazer-publisher-sdk to crates.io

on:
push:
tags:
- rust-pyth-lazer-publisher-sdk-v*
jobs:
publish-pyth-lazer-publisher-sdk:
name: Publish Rust package pyth-lazer-publisher-sdk to crates.io
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2

- run: cargo publish --token ${CARGO_REGISTRY_TOKEN}
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
working-directory: "lazer/publisher-sdk/rust"
80 changes: 80 additions & 0 deletions lazer/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions lazer/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]
resolver = "2"
members = [
"publisher_sdk/rust",
"sdk/rust/protocol",
"sdk/rust/client",
"contracts/solana/programs/pyth-lazer-solana-contract",
Expand Down
59 changes: 59 additions & 0 deletions lazer/publisher_sdk/proto/publisher_update.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
syntax = "proto3";

import "google/protobuf/timestamp.proto";

package pyth_lazer_transaction;

// PublisherUpdate contains an array of individual updates and a timestamp
message PublisherUpdate {
// Array of updates, each of which target a single feed
repeated FeedUpdate updates = 1;

// ID of the Publisher that is sending the update
// Should match ID stored in Pyth Lazer
optional uint32 publisher_id = 2;

// Timestamp when this message was created
optional google.protobuf.Timestamp publisher_timestamp = 3;
}

// Update to a feed. May contain different types of data depending on what kind of update it is
message FeedUpdate {
// Feed which the update should be applied to
// Should match a feed id recognized by PythLazer
optional uint32 feed_id = 1;

// Timestamp when this data was first acquired or generated
optional google.protobuf.Timestamp source_timestamp = 2;

// one of the valid updates allowed by publishers for a lazer feed
oneof update {
PriceUpdate price_update = 3;
FundingRateUpdate funding_rate_update = 4;
};
}

message PriceUpdate {
// Price for the symbol as an integer
// Should be produced with a matching exponent to the configured exponent value in PythLazer
// May be missing if no price data is available
optional int64 price = 1;

// Best Bid Price for the symbol as an integer
// Should be produced with a matching exponent to the configured exponent value in PythLazer
// May be missing if no data is available
optional int64 best_bid_price = 2;

// Best Ask Price for the symbol as an integer
// Should be produced with a matching exponent to the configured exponent value in PythLazer
// May be missing if no data is available
optional int64 best_ask_price = 3;
}

message FundingRateUpdate {
// Price for which the funding rate applies to
optional int64 price = 1;

// Perpetual Future funding rate
optional int64 rate = 2;
}
34 changes: 34 additions & 0 deletions lazer/publisher_sdk/proto/pyth_lazer_transaction.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
syntax = "proto3";

package pyth_lazer_transaction;

import "publisher_update.proto";

// Types of Signatures allowed for signing Lazer Transactions
enum TransactionSignatureType {
// signature is 64 bytes long
ed25519 = 0;
}

// Signed lazer transaction payload
// This is what Pyth Lazer expects as input to the system
message SignedLazerTransaction {
// Type and signature should match
optional TransactionSignatureType signature_type = 1;

// Signature derived by signing payload with private key
optional bytes signature = 2;

// a LazerTransaction message which is already encoded with protobuf as bytes
// The encoded bytes are what should be signed
optional bytes payload = 3;
}
Comment on lines +15 to +25
Copy link
Contributor

Choose a reason for hiding this comment

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

After yesterday's discussion I realized that this structure is not very well suited for governance mesages that are based on wormhole. Maybe we need something like this?

// replaces old SignedLazerTransaction and LazerTransaction
message LazerTransaction {
    oneof message {
        PublisherUpdateTransaction publisher_update = 1;
        GovernanceTransaction governance = 2;
    }
}
message PublisherUpdateTransaction {
    optional bytes signature = 1;
    // protobuf-encoded PublisherUpdate
    optional bytes payload = 2;
}
message GovernanceTransaction {
    // encoded in wormhole format, with guardian signatures inside
    optional bytes payload = 1;
}

Copy link
Collaborator

@ali-behjati ali-behjati Apr 11, 2025

Choose a reason for hiding this comment

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

I like to have a fixed transaction layout and then have customisability in the payload layer. I see transaction as something for sequencer and content of it as something for the aggregator. It's good to have permissioning over who does what. And then, you can say that the sequencer only streams txs from verified pubkeys. Otherwise, you'd need to either think of continuing the tokenized gateway (which is kind of like sigs) or verifying wh messages (which is very expensive)


// Transaction contianing one of the valid Lazer Transactions
message LazerTransaction {
oneof payload {
// Expected transaction sent by Publishers
// May contain many individual updates to various feeds
PublisherUpdate publisher_update = 1;
}
}
14 changes: 14 additions & 0 deletions lazer/publisher_sdk/rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "pyth-lazer-publisher-sdk"
version = "0.1.0"
edition = "2021"
description = "Pyth Lazer Publisher SDK types."
license = "Apache-2.0"
repository = "https://github.com/pyth-network/pyth-crosschain"
build = "build.rs"

[dependencies]
protobuf = "3.7.2"

[build-dependencies]
protobuf-codegen = "3.7.2"
19 changes: 19 additions & 0 deletions lazer/publisher_sdk/rust/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use std::io::Result;

/// Automatically runs during cargo build.
/// Proto files for Lazer are defined in the lazer sdk folder in the proto/ subdirectory.
/// Both JS and Rust SDKs read the proto files for generating types.
fn main() -> Result<()> {
// Tell cargo to recompile if any .proto files change
println!("cargo:rerun-if-changed=../proto/");

protobuf_codegen::Codegen::new()
.pure()
.include("../proto")
.input("../proto/publisher_update.proto")
.input("../proto/pyth_lazer_transaction.proto")
.cargo_out_dir("protobuf")
.run_from_script();

Ok(())
}
7 changes: 7 additions & 0 deletions lazer/publisher_sdk/rust/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pub mod transaction {
pub use crate::protobuf::pyth_lazer_transaction::*;
}

mod protobuf {
include!(concat!(env!("OUT_DIR"), "/protobuf/mod.rs"));
}
18 changes: 18 additions & 0 deletions lazer/sdk/js/turbo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"$schema": "https://turbo.build/schema.json",
"extends": ["//"],
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"build:cjs": {
"dependsOn": ["build"],
"outputs": ["dist/cjs/**"]
},
"build:esm": {
"dependsOn": ["build"],
"outputs": ["dist/esm/**"]
}
}
}
Loading
Loading