Skip to content
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
66 changes: 66 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: CI

on:
pull_request:
workflow_dispatch:

env:
SUI_VERSION: ${{ vars.SUI_VERSION || 'mainnet' }}

jobs:
install-sui:
runs-on: ubuntu-latest
steps:
- name: Install suiup
run: curl -sSfL https://raw.githubusercontent.com/MystenLabs/suiup/main/install.sh | sh
- name: Install Sui CLI
run: ~/.local/bin/suiup install sui@${SUI_VERSION} --yes
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload Sui CLI
uses: actions/upload-artifact@v4
with:
name: sui-cli-${{ env.SUI_VERSION }}
path: ~/.local/bin/sui

build:
runs-on: ubuntu-latest
needs: install-sui
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: sui-cli-${{ env.SUI_VERSION }}
path: .sui/bin
- run: chmod +x .sui/bin/sui
- run: .sui/bin/sui move build --build-env mainnet --path alphafi_stdlib
- run: .sui/bin/sui move build --build-env mainnet --path alphafi_oracle
- run: .sui/bin/sui move build --build-env mainnet --path alpha_lending

lint:
runs-on: ubuntu-latest
needs: install-sui
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: sui-cli-${{ env.SUI_VERSION }}
path: .sui/bin
- run: chmod +x .sui/bin/sui
- run: .sui/bin/sui move build --lint --build-env mainnet --path alphafi_stdlib
- run: .sui/bin/sui move build --lint --build-env mainnet --path alphafi_oracle
- run: .sui/bin/sui move build --lint --build-env mainnet --path alpha_lending

test:
runs-on: ubuntu-latest
needs: install-sui
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: sui-cli-${{ env.SUI_VERSION }}
path: .sui/bin
- run: chmod +x .sui/bin/sui
- run: .sui/bin/sui move test --build-env mainnet --path alphafi_stdlib
- run: .sui/bin/sui move test --build-env mainnet --path alphafi_oracle
- run: .sui/bin/sui move test --build-env mainnet --path alpha_lending -i 50000000000
2 changes: 1 addition & 1 deletion alpha_lending/Move.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ alphafi_oracle = { local = "../alphafi_oracle" }
BluefinSpot = { git = "https://github.com/fireflyprotocol/bluefin-spot-contract-interface.git", rev = "main"}
CetusClmm = { git = "https://github.com/CetusProtocol/cetus-clmm-interface.git", subdir = "sui/cetus_clmm", rev = "main" }
IntegerMate = { git = "https://github.com/CetusProtocol/integer-mate.git", rev = "main", override = true }
sui_system = { git = "https://github.com/MystenLabs/sui.git", rev = "mainnet", subdir = "crates/sui-framework/packages/sui-system", override = true }
# MoveStdlib = { git = "https://github.com/MystenLabs/sui.git", rev = "mainnet-v1.49.2", subdir = "crates/sui-framework/packages/move-stdlib", override=true}
# Sui = { git = "https://github.com/MystenLabs/sui.git", rev = "mainnet-v1.49.2", subdir = "crates/sui-framework/packages/sui-framework", override=true }
# Bridge = { git = "https://github.com/MystenLabs/sui.git", rev = "mainnet-v1.49.2", subdir = "crates/sui-framework/packages/bridge", override=true}
Expand Down Expand Up @@ -48,4 +49,3 @@ cetus_clmm = "0x1eabed72c53feb3805120a081dc15963c204dc8d091542592abaf7a35689b2fb
#alphafi_oracle = "0x123456"
#alphafi_stdlib = "0x123457"
#mmt_v3 = "0x123458"

4 changes: 4 additions & 0 deletions alphafi_oracle/Move.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ rev = "sui-contract-mainnet"
[dependencies.alphafi_stdlib]
local = "../alphafi_stdlib"

# Pyth Lazer ABI stub — provides the `pyth_lazer` named address (real pkg linked on-chain via published-at).
[dependencies.pyth_lazer]
local = "../deps-mainnet/lazer_interface"


#[dependencies]
#alphafi_stdlib = { local = "../alphafi_stdlib" }
Expand Down
60 changes: 58 additions & 2 deletions alphafi_oracle/sources/oracle.move
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ module alphafi_oracle::oracle {
use alphafi_stdlib::math::{Self,Number};
use sui::math as sui_math;
use sui::dynamic_field;
use pyth_lazer::update_v2::Update as LazerUpdate;


#[error]
Expand Down Expand Up @@ -44,6 +45,27 @@ module alphafi_oracle::oracle {
last_updated: u64
}

// Mirrors the implementation event in the alphalend-contracts repo (alphafi_oracle/sources/oracle.move)
// — that repo is the source of truth; keep fields byte-identical to it. conf is the real spot confidence
// band (price units), unlike PythPriceUpdationEvent.conf which is a vestigial constant 0 — indexers must
// branch on event TYPE, not the shared field name.
public struct LazerPriceUpdationEvent has copy, drop {
coin_type: TypeName,
feed_id: u32,
price: Number,
ema_price: Number,
conf: Number,
last_updated: u64,
}

public struct LazerPriceSkippedEvent has copy, drop {
coin_type: TypeName,
feed_id: u32,
reason: u8,
price: Number,
ema_price: Number,
}

public struct AdminCap has store, key {
id: UID
}
Expand Down Expand Up @@ -89,7 +111,15 @@ module alphafi_oracle::oracle {
self.ema_price
}


// Confidence band (price units) of the last update; meaningful only for Lazer-sourced prices
// (the Pyth writer stores 0).
public fun get_conf(
self: &PriceInfo,
) : Number{
self.conf
}


public fun coin_type(
self: &PriceInfo,
) : TypeName{
Expand All @@ -103,14 +133,40 @@ module alphafi_oracle::oracle {
self.last_updated
}


// ---- Lazer config getters (stubbed; see alphafi_oracle::oracle for the real bodies) ----

public fun is_lazer_enabled(_self: &Oracle): bool {
abort 0
}

public fun get_lazer_feed_ids_for_coin(_self: &Oracle, _coin_type: TypeName): vector<u32> {
abort 0
}

public fun get_coin_type_for_lazer_feed_id(_self: &Oracle, _feed_id: u32): TypeName {
abort 0
}

public fun get_all_supported_price_lazer_identifiers(_self: &Oracle): vector<u32> {
abort 0
}


public fun update_price_from_pyth(
self: &mut Oracle,
price_info_object: &PriceInfoObject,
clock: &Clock,
) {
abort 0
}

public fun ingest_lazer_update(
self: &mut Oracle,
update: LazerUpdate,
clock: &Clock,
) {
abort 0
}


}
26 changes: 26 additions & 0 deletions deps-mainnet/lazer_interface/Move.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "pyth_lazer"
edition = "2024"
# ABI stub for Pyth Lazer TYPES (publish with --skip-dependency-verification; consumer links the real pkg).
# Verification runs in the PTB, so the oracle consumes types only and NEVER calls the verifier fn — meaning
# future Pyth verifier upgrades need NO oracle republish. The `update_v2` module is new in the v2 upgrade,
# so published-at must point at a version that CONTAINS it. The `pyth_lazer` named address below remains the
# original package id; normalized mainnet modules/types fetched through the v2 package still report that id.
# If these ids are wrong, publish/link fails or the Update type mismatches instead of silently binding a fake type.
# MAINNET (verified on-chain 2026-07-06): published-at 0xefbf…ee10 (v2, has update_v2/feed +
# parse_and_verify_le_ecdsa_update_v2); original/runtime id 0x7b50…f580.
# At publish, confirm generated Move.lock records pyth_lazer's original-published-id as 0x7b50…f580
# if the active toolchain emits that field.
# TESTNET: Lazer is STILL v1 (no update_v2 module) at 0xf5bd…8c21 — a testnet build will FAIL to link
# until Pyth upgrades it; point published-at at that v2 id (or a self-published v2).
# MAINNET (active):
published-at = "0xefbfd064480777699fd9c557a5804d72ace7bc82661fdc8d1f1a44ea6d92ee10"
# TESTNET (uncomment + set a self-published v2 id when republishing on testnet; Pyth's testnet Lazer
# 0xf5bd…8c21 is still v1 and lacks update_v2):
# published-at = "0x0000000000000000000000000000000000000000000000000000000000000000"

[dependencies]
# sui + std only (no Wormhole).

[addresses]
pyth_lazer = "0x7b502c8a7bcb3915892347f11086745570e759fe9708d03c03accf4c90bbf580"
50 changes: 50 additions & 0 deletions deps-mainnet/lazer_interface/sources/interface.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// ABI stub for the `pyth_lazer` TYPES that alphafi_oracle consumes — `abort` bodies that NEVER run: the
// consumer links Pyth's real on-chain Lazer package via `published-at` in Move.toml (publish with
// --skip-dependency-verification). This supplies only type identities (no Wormhole dep).
//
// Verification is NOT here: per Pyth's "verify in PTBs, not contracts" guidance, the PTB calls the real
// on-chain `pyth_lazer::parse_and_verify_le_ecdsa_update_v2` and passes the verified `Update` into
// `alphafi_oracle::oracle::ingest_lazer_update`. So this stub needs no `pyth_lazer`/`state` module and the
// oracle never links the verifier fn — it survives Pyth verifier upgrades without a republish.
// Mirrors pyth-network/pyth-crosschain @ lazer/contracts/sui — re-sync only if these consumed types change.

module pyth_lazer::i64 {
public struct I64 has copy, drop, store {
negative: bool,
magnitude: u64,
}
public fun get_is_negative(_i: &I64): bool { abort 0 }
public fun get_magnitude_if_positive(_i: &I64): u64 { abort 0 }
}

module pyth_lazer::i16 {
public struct I16 has copy, drop, store {
negative: bool,
magnitude: u16,
}
public fun get_is_negative(_i: &I16): bool { abort 0 }
public fun get_magnitude_if_positive(_i: &I16): u16 { abort 0 }
public fun get_magnitude_if_negative(_i: &I16): u16 { abort 0 }
}

module pyth_lazer::feed {
use pyth_lazer::i64::I64;
use pyth_lazer::i16::I16;
// NEVER add `store`: by-value consumption inside the verifying PTB is the security model.
public struct Feed has copy, drop {}
public fun feed_id(_feed: &Feed): u32 { abort 0 }
public fun price(_feed: &Feed): Option<Option<I64>> { abort 0 }
public fun exponent(_feed: &Feed): Option<I16> { abort 0 }
public fun ema_price(_feed: &Feed): Option<Option<I64>> { abort 0 }
public fun confidence(_feed: &Feed): Option<Option<I64>> { abort 0 }
public fun ema_confidence(_feed: &Feed): Option<Option<u64>> { abort 0 }
public fun feed_update_timestamp(_feed: &Feed): Option<Option<u64>> { abort 0 }
}

module pyth_lazer::update_v2 {
use pyth_lazer::feed::Feed;
// NEVER add `store`: by-value consumption inside the verifying PTB is the security model.
public struct Update has copy, drop {}
public fun timestamp(_update: &Update): u64 { abort 0 }
public fun feeds(_update: &Update): vector<Feed> { abort 0 }
}
Loading