Pythacoin – CDP stablecoin on Cardano using Pyth Lazer#124
Pythacoin – CDP stablecoin on Cardano using Pyth Lazer#124nau wants to merge 1 commit intopyth-network:mainfrom
Conversation
| case _ => throw RuntimeException("Owner address must have a key credential") | ||
| case _ => throw RuntimeException("Owner address must be a Shelley address") | ||
| Log.info(s"Owner PKH: ${ownerPkh.hash.toHex}") | ||
| val nftName = AssetName(ByteString.fromString("CDP-" + System.currentTimeMillis())) |
There was a problem hiding this comment.
🔴 CDP NFT name uses System.currentTimeMillis() instead of UTxO-derived hash, risking name collisions
The server generates CDP NFT names using ByteString.fromString("CDP-" + System.currentTimeMillis()), but the docstring at CdpTransactions.scala:38-39 specifies names should be "derived from sha2_256(firstInput.txId ++ firstInput.index)". The timestamp approach is not guaranteed unique — two concurrent Open CDP requests arriving at the same millisecond produce identical NFT names. This creates duplicate tokens that break CDP identification: CdpQueries.findCdpUtxo (CdpQueries.scala:37-46) returns the first matching UTxO, so subsequent operations (borrow, repay, close, liquidate) could be applied to the wrong CDP. The on-chain validator checks owner signatures for borrow/repay/close (mitigating those), but the Liquidate handler at CdpValidator.scala:278-299 requires no owner signature, so a liquidation could target the wrong CDP if both share a name and one is above the 90% LTV threshold.
Prompt for agents
In lazer/cardano/pythacoin/src/main/scala/pythacoin/Server.scala line 202, replace the timestamp-based NFT name generation with a UTxO-derived approach as documented in CdpTransactions.scala. The fix should:
1. First query the owner's UTxOs to find one that will be consumed as a transaction input
2. Derive the NFT name from a hash of that UTxO's transaction ID and index: sha2_256(txId ++ index)
3. Use this deterministic, unique name as the AssetName
For example:
val ownerUtxos = ctx.provider.findUtxos(ownerAddr).await(30.seconds).getOrElse(throw ...)
val firstInput = ownerUtxos.head._1
val uniqueBytes = java.security.MessageDigest.getInstance("SHA-256").digest(
firstInput.transactionId.bytes ++ BigInt(firstInput.index).toByteArray
)
val nftName = AssetName(ByteString.unsafeFromArray(uniqueBytes))
This ensures each CDP gets a globally unique NFT name derived from the consumed UTxO, matching the documented behavior at CdpTransactions.scala:38-39.
Was this helpful? React with 👍 or 👎 to provide feedback.
Pyth Examples Contribution
Team Information
Type of Contribution
Project Information
Project/Example Name: Pythacoin – CDP-based stablecoin on Cardano using Pyth Lazer
Pyth Product Used:
Blockchain/Platform:
Description
What does this contribution do?
Pythacoin is a fully functional CDP (Collateralized Debt Position) stablecoin protocol on Cardano. Users lock ADA as collateral to mint PUSD, a synthetic USD-pegged stablecoin. The protocol enforces a 95% max LTV for minting and a 90% liquidation threshold — when a CDP becomes undercollateralized, anyone can liquidate it.
How does it integrate with Pyth?
solanaformat) and includes them as withdrawal redeemers in every price-dependent transaction (open, borrow, liquidate).What problem does it solve or demonstrate?
Demonstrates a complete DeFi lending protocol on Cardano powered by Pyth Lazer price feeds — from on-chain binary payload parsing to off-chain transaction building to a web frontend with wallet integration.
Directory Structure
Testing & Verification
How to Test This Contribution
Prerequisites
Setup & Run Instructions
Deployment Information
Network: Cardano PreProd
Demo: https://youtu.be/RK7WsZQiG54
Checklist
Code Quality
Testing
Additional Context
Screenshots/Demo
Demo video: https://youtu.be/RK7WsZQiG54
Notes for Reviewers