Lazer Perps: Cardano perpetual futures with Pyth Lazer price feeds#104
Open
mariaelisaaraya wants to merge 20 commits intopyth-network:mainfrom
Open
Lazer Perps: Cardano perpetual futures with Pyth Lazer price feeds#104mariaelisaaraya wants to merge 20 commits intopyth-network:mainfrom
mariaelisaaraya wants to merge 20 commits intopyth-network:mainfrom
Conversation
On-chain (Aiken): - lib/lazer_perps/types.ak — Position, Direction, PerpsConfig - lib/lazer_perps/oracle_gate.ak — pyth.get_updates() price reader - validators/open_position.ak — entry price + leverage check - validators/close_position.ak — PnL settlement with owner sig - validators/liquidate.ak — margin ratio check, anyone can trigger Off-chain (TypeScript): - orchestrator.ts — central Pyth Lazer price witness fetcher - open_position.ts, close_position.ts, liquidate.ts — tx builders - keeper.ts — WebSocket price monitor for liquidation opportunities - feeds.ts — RWA market catalog Verified tx: dbf5336fafdab45c8ee287f9409d4e81dfee6375e0c4dd42e89a6b523c557dd5
Validators parameterized with MarketConfig (feed_id, leverage_cap, min_collateral, pool_policy_id) — deploy per market without recompiling. lib/lazer_perps/: - types.ak — MarketConfig, Position, PoolState, Direction, constants - oracle_gate.ak — pyth.get_updates() price reader (unchanged) validators/: - open_position.ak — entry price match + leverage cap + min collateral - close_position.ak — owner-only close + PnL formula + oracle price - liquidate.ak — margin ratio < 80% check + 1% keeper fee - pool_manager.ak — liquidity pool + open interest caps + settlement
PositionDatum {
feed_id, direction, leverage, entry_price (6 decimals),
collateral (micro USDCx), trader_pkh, opened_at (POSIXTime)
}
Updated all validators to use PositionDatum instead of Position.
PnL now computed as: size = collateral * leverage.
New lib/lazer_perps/formulas.ak with: - compute_pnl: dir_multiplier × collateral × leverage × (exit - entry) / entry - compute_payout: max(0, collateral + PnL) - liquidation_price_long: entry × (1 - 1/leverage + 0.01) - liquidation_price_short: entry × (1 + 1/leverage - 0.01) - is_liquidatable: Long if price <= liq_price, Short if price >= liq_price close_position.ak now computes PnL and payout on close. liquidate.ak now uses exact liquidation price formula instead of margin ratio.
feeds.ts — market catalog with leverage caps per market orchestrator.ts — central module: - fetchPriceWitness(): signed price + Pyth state resolution - streamPythPrice(): WebSocket stream for keeper - buildOpenPositionTx/buildClosePositionTx/buildLiquidateTx - all builders apply zero-withdrawal price witness open_position.ts — uses orchestrator.buildOpenPositionTx close_position.ts — uses orchestrator.buildClosePositionTx liquidate.ts — uses orchestrator.buildLiquidateTx keeper.ts — streams prices, computes liq prices, detects triggers Verified tx: 38a5e8391b36f814f1fca788f10b1bb91063e801ae29fa829829d011276000fe
Add collateral.ts — USDCx as Cardano native token (policy ID + asset name). Uses ADA on PreProd (USDCX_POLICY_ID env var for mainnet). All amounts in micro USDCx (6 decimals). On-chain: CollateralToken type in MarketConfig (policy_id + asset_name). Off-chain: formatCollateral(), parseCollateral(), collateralUnit(). Verified tx: 9b6e5352bc00c7fe9869cec9abd2aef253de09894d117d218ea0f9c0a9872eb9
Tests (all passing): - pnl_long_profit, pnl_long_loss, pnl_short_profit, pnl_short_loss - payout_positive, payout_zero_floor - liq_price_long_10x, liq_price_short_10x - liquidatable_long_yes/no, liquidatable_short_yes/no Fix: remove unused keeper_fee_bps import from liquidate.ak
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Pyth Examples Contribution
Team Name: Lazer Perps (ex Lazer RWA)
Submission Name: lazer-perps
Team Members: María Elisa Araya (@ar3lisa), Bárbara Olivera (@b4rbbb)
Contact: araya3lisa@gmail.com / brb.olivera@gmail.com
Type of Contribution
Project Information
Project/Example Name: Lazer Perps
Pyth Product Used:
Blockchain/Platform:
Description
What does this contribution do?
Perpetual futures protocol on Cardano. Traders open leveraged long/short positions on RWA assets (Gold, Silver, Oil) using USDCx as collateral. Every action (open, close, liquidate) requires a fresh Pyth Lazer price witness in the transaction. No price → no trade.
How does it integrate with Pyth?
oracle_gate.akcallspyth.get_updates(pyth_id, self)frompyth-network/pyth-lazer-cardanoto read verified prices on-chain. All validators (open_position, close_position, liquidate, pool_manager) read prices through this gate. Off-chain,orchestrator.tsfetches signed updates from Pyth Lazer and embeds them using the zero-withdrawal pattern.What problem does it solve or demonstrate?
Demonstrates how to build a DeFi protocol (perpetual futures) that requires fresh oracle prices for every on-chain action. The oracle gate pattern ensures no trade can happen without a verified Pyth price witness.
Validators
oracle_gate.akpyth.get_updates()open_position.akclose_position.akliquidate.akpool_manager.akTesting & Verification
How to Test This Contribution
Prerequisites
Verified transactions on PreProd
ccb967d2...6532eebc...477bfe3f...Deployment Information
Deployed on Cardano PreProd testnet. Pyth Policy ID:
d799d287105dea9377cdf9ea8502a83d2b9eb2d2050a8aea800a21e6Checklist
Code Quality
Testing
aiken check)Additional Context
Notes for Reviewers
Direct extension of our Lazer RWA submission. Terminal output captures are in the
screenshots/folder.