Skip to content

feat(cardano): LemonPyth - RWA Peg Defense with Aiken & Pyth Lazer#105

Open
MattSZdev wants to merge 2 commits intopyth-network:mainfrom
MattSZdev:main
Open

feat(cardano): LemonPyth - RWA Peg Defense with Aiken & Pyth Lazer#105
MattSZdev wants to merge 2 commits intopyth-network:mainfrom
MattSZdev:main

Conversation

@MattSZdev
Copy link
Copy Markdown

@MattSZdev MattSZdev commented Mar 22, 2026

Pyth Examples Contribution

Type of Contribution

  • New Example Project (Adding a new example to demonstrate Pyth integration)
  • Bug Fix (Fixing an issue in existing examples)
  • Documentation Update (Improving README, comments, or guides)
  • Enhancement (Improving existing functionality or adding features)
  • Hackathon Submission (Submitting a project from a hackathon)

Project Information

Project/Example Name:

Pyth Product Used:

  • Pyth Price Feeds
  • Pyth Entropy
  • Multiple Products
  • Other: ___________

Blockchain/Platform:

  • Ethereum/EVM
  • Solana
  • Aptos
  • Sui
  • Fuel
  • Starknet
  • TON
  • Other: ___________

Description

What does this contribution do?

How does it integrate with Pyth?

What problem does it solve or demonstrate?

Directory Structure (for new examples)

[product]/[example-name]/
├── contract/          # Smart contracts (if applicable)
├── app/              # Frontend application (if applicable)  
├── README.md         # Project documentation
└── ...

Testing & Verification

How to Test This Contribution

Prerequisites

  • Node.js version: ___
  • Other dependencies: ___

Setup & Run Instructions

# Add your setup and run commands here
cd [path-to-your-example]
npm install
# ... other commands

Deployment Information (if applicable)

Network:

Contract Address(es):

Demo URL:

Checklist

Code Quality

  • Code follows existing patterns in the repository
  • Proper error handling implemented
  • No hardcoded values (use environment variables where appropriate)

Testing

  • Tested locally and works as expected
  • All existing functionality still works (no breaking changes)

Additional Context

Related Issues

Fixes #

Screenshots/Demo (if applicable)

Notes for Reviewers


Thank you for contributing to Pyth Examples!


Open with Devin

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 6 potential issues.

View 7 additional findings in Devin Review.

Open in Devin Review

Comment on lines +7 to +9
{ hex: "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43", symbol: 'BTC', network: 'Bitcoin' },
{ hex: "0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace", symbol: 'ETH', network: 'Ethereum' },
{ hex: "0x2a01deaec9e51a579277b34b122399984d0bbf57e2458a7e42fecd2829867a0d", symbol: 'ADA', network: 'Cardano' }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 Pyth Hermes API feed ID lookup always fails due to '0x' prefix mismatch

The FEEDS array stores hex IDs with a 0x prefix (e.g. "0xe62df6c8...") but the Pyth Hermes API (/v2/updates/price/latest) returns item.id without the 0x prefix (e.g. "e62df6c8..."). As confirmed by existing examples in this repo (price_feeds/evm/oracle_swap/app/src/App.tsx:26), Pyth price feed IDs are stored/returned without the prefix. The comparison FEEDS.find(f => f.hex === item.id) on line 39 therefore always returns undefined, causing the spread ...asset to contribute no properties. The resulting data objects will be missing symbol, hex, and network — breaking the ScreenerTable display (which keys on row.symbol) and all downstream components that reference symbol.

Suggested change
{ hex: "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43", symbol: 'BTC', network: 'Bitcoin' },
{ hex: "0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace", symbol: 'ETH', network: 'Ethereum' },
{ hex: "0x2a01deaec9e51a579277b34b122399984d0bbf57e2458a7e42fecd2829867a0d", symbol: 'ADA', network: 'Cardano' }
{ hex: "e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43", symbol: 'BTC', network: 'Bitcoin' },
{ hex: "ff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace", symbol: 'ETH', network: 'Ethereum' },
{ hex: "2a01deaec9e51a579277b34b122399984d0bbf57e2458a7e42fecd2829867a0d", symbol: 'ADA', network: 'Cardano' }
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +252 to +253
AcceptAgreement buyer ->
traceIfFalse "NOT_BUYER" (isSigned buyer info) &&
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 AcceptAgreement validator uses untrusted redeemer-supplied buyer instead of datum's buyer

The AcceptAgreement case at line 252-253 checks isSigned buyer info where buyer comes from the redeemer (user-supplied data). Any user can supply their own PubKeyHash as the buyer field in the redeemer, sign the transaction, and pass this check — even if they are not the designated buyer in the datum. This contrasts with the DepositFunds case at SmartSupplyLedger.hs:261 which correctly uses ssdBuyer dat from the on-chain datum. The fix should be: isSigned (ssdBuyer dat) info instead of isSigned buyer info.

Suggested change
AcceptAgreement buyer ->
traceIfFalse "NOT_BUYER" (isSigned buyer info) &&
AcceptAgreement buyer ->
traceIfFalse "NOT_BUYER" (isSigned (ssdBuyer dat) info) &&
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +58 to +61
const validator = {
type: "PlutusV2",
script: validatorEntry.compiledCode,
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 Plutus version mismatch: Aiken compiles PlutusV3 but JS code creates PlutusV2 validator

The Aiken blueprint at src/assets/plutus.json:4 declares "plutusVersion": "v3", but both CardanoService.js:38 and SmartSupplyPanel.jsx:59 construct the validator with type: "PlutusV2". In Lucid, the type field determines how the script hash is computed — a PlutusV2 hash differs from a PlutusV3 hash for the same compiled code. This mismatch causes the computed scriptAddress to differ from the actual on-chain script address, meaning any transaction built with this validator will target the wrong address and fail.

Suggested change
const validator = {
type: "PlutusV2",
script: validatorEntry.compiledCode,
};
const validator = {
type: "PlutusV3",
script: validatorEntry.compiledCode,
};
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +37 to +40
const validator = {
type: "PlutusV2",
script: validatorEntry.compiledCode,
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 Plutus version mismatch in CardanoService.js: PlutusV2 instead of PlutusV3

Same issue as in SmartSupplyPanel.jsx — CardanoService.js:38 uses type: "PlutusV2" but the Aiken contract in src/assets/plutus.json:4 is compiled as PlutusV3. This will produce an incorrect script hash and address, causing all transactions to fail.

Suggested change
const validator = {
type: "PlutusV2",
script: validatorEntry.compiledCode,
};
const validator = {
type: "PlutusV3",
script: validatorEntry.compiledCode,
};
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

const lib = await Lucid.new(
new Blockfrost(
"https://cardano-preprod.blockfrost.io/api/v0",
"preprodW9VepmQEx3r9LPr3QncreNvkBDApZD8m"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 Hardcoded Blockfrost API key committed to source control

The Blockfrost project API key "preprodW9VepmQEx3r9LPr3QncreNvkBDApZD8m" is hardcoded directly in the source at line 22. This key will be visible in the public repository, allowing anyone to consume the project's Blockfrost rate limits or potentially abuse it. The .gitignore already excludes .env files, suggesting the intent was to use environment variables. The key should be loaded from import.meta.env.VITE_BLOCKFROST_KEY or similar.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

pythPrice: price,
confidence: Number(item.price.conf) * Math.pow(10, item.price.expo),
extPrice: price * 1.0002,
gap: (Math.random() * 0.05), // Variación pequeña para visualización
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Arbitrage gap is always a positive random value, making BEARISH regime impossible

At line 46, gap is computed as Math.random() * 0.05, which always produces a value in [0, 0.05). This means the gap field is never negative. Downstream in Screener.jsx:24, the regime classification asset.gap < -0.05 ? 'BEARISH' : 'LATERAL' can never evaluate to 'BEARISH' since gap is never negative. Furthermore, since the max value of Math.random() * 0.05 is just under 0.05, asset.gap > 0.05 is also (almost) never true, so 'BULLISH' is nearly impossible too — all assets will always show as 'LATERAL'.

Suggested change
gap: (Math.random() * 0.05), // Variación pequeña para visualización
gap: (Math.random() - 0.5) * 0.2, // Variación pequeña para visualización
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant