Conversation
| let datum_ok = | ||
| new_d.feed_id == xau_usd_feed_id && new_d.relayer == d.relayer && new_d.open_price == d.open_price && new_d.close_price == Some( | ||
| close_price, | ||
| ) && new_d.status == new_status && new_d.deadline_ms == d.deadline_ms && new_d.bets == d.bets && new_d.claimed == [] | ||
|
|
||
| after_deadline && relayer_signed && datum_ok |
There was a problem hiding this comment.
🔴 Settle action has no value-preservation check, allowing the relayer to drain the entire pool
The Settle handler (lines 117–153) checks after_deadline && relayer_signed && datum_ok but, unlike Claim (round_validator.ak:189) and Refund (round_validator.ak:228), it never verifies that the continued script output retains the same lovelace as the input. A malicious relayer can build a settlement transaction whose continued script output holds just the Cardano minimum UTxO (~2 ADA), directing the remaining pool lovelace to their own wallet. The datum passes validation because it only checks logical fields, not value. This completely drains bettor funds before any claims or refunds can occur.
| let datum_ok = | |
| new_d.feed_id == xau_usd_feed_id && new_d.relayer == d.relayer && new_d.open_price == d.open_price && new_d.close_price == Some( | |
| close_price, | |
| ) && new_d.status == new_status && new_d.deadline_ms == d.deadline_ms && new_d.bets == d.bets && new_d.claimed == [] | |
| after_deadline && relayer_signed && datum_ok | |
| let datum_ok = | |
| new_d.feed_id == xau_usd_feed_id && new_d.relayer == d.relayer && new_d.open_price == d.open_price && new_d.close_price == Some( | |
| close_price, | |
| ) && new_d.status == new_status && new_d.deadline_ms == d.deadline_ms && new_d.bets == d.bets && new_d.claimed == [] | |
| let script_value_ok = lovelace_of(cont_out.value) >= own_lovelace | |
| after_deadline && relayer_signed && datum_ok && script_value_ok |
Was this helpful? React with 👍 or 👎 to provide feedback.
| expect Some(bet) = | ||
| list.find(d.bets, fn(b) { b.bettor == bettor && b.side == winner }) |
There was a problem hiding this comment.
🔴 Bettors who place multiple bets permanently lose funds from all but the first bet
Nothing in PlaceBet (round_validator.ak:79-103) prevents the same bettor from placing multiple bets. However, Claim uses list.find (round_validator.ak:171) which only returns the first matching bet, and then adds the bettor to the claimed list (round_validator.ak:183-186). The not_yet_claimed guard (round_validator.ak:168) blocks any subsequent claim attempt. The same issue exists for Refund (round_validator.ak:214). This means a bettor's second, third, etc. bets are permanently locked in the script with no way to recover them.
Example scenario
Bettor A places two 5-ADA bets on Up. Up wins. Claim finds the first 5-ADA bet, pays out based on it, and marks bettor A as claimed. The second 5-ADA bet's stake is irrecoverable.
Prompt for agents
There are two possible approaches to fix this:
Option A (simpler): Prevent duplicate bets. In PlaceBet (round_validator.ak:79-103), add a check that the bettor does not already have a bet in d.bets. For example, add: let no_existing_bet = !list.any(d.bets, fn(b) { b.bettor == bettor }) and include it in the final conjunction on line 102.
Option B (more flexible): Aggregate all matching bets during Claim/Refund. In Claim (round_validator.ak:170-175), instead of list.find to get a single bet, use list.foldl over d.bets to sum the amounts of all bets where b.bettor == bettor && b.side == winner. Similarly in Refund (round_validator.ak:214), aggregate all bets where b.bettor == bettor. Then calculate the payout based on the total aggregated stake.
Was this helpful? React with 👍 or 👎 to provide feedback.
Pyth Examples Contribution
Type of Contribution
Project Information
5minBets:
Pyth Product Used:
Blockchain/Platform:
Description
What does this contribution do?
This contribution adds
5minBets, a Cardano-native price prediction market concept written in Aiken. The project models a simple 5-minute betting flow where users betUPorDOWNon theXAU/USDprice, with the current repository containing an initial validator prototype and a README that describes the intended protocol design and architecture.How does it integrate with Pyth?
The project is designed to use Pyth price data as the oracle source for round creation and settlement. A relayer records the opening
XAU/USDprice from Pyth, users place bets during the round, and the closing Pyth price is later used to determine the winning side and calculate payouts.What problem does it solve or demonstrate?
This example demonstrates how Pyth-powered market data can be used in a Cardano application to drive outcome-based logic for a prediction market. It also serves as a hackathon prototype and starting point for building a more complete oracle-driven betting protocol in Aiken.
Directory Structure (for new examples)
Testing & Verification
How to Test This Contribution
This example is currently an Aiken project prototype. Reviewers can validate formatting, buildability, and test execution using the standard Aiken workflow.
Prerequisites
Setup & Run Instructions
cd pyth-examples/lazer/cardano/5minBets aiken fmt --check aiken check -D aiken buildDeployment Information (if applicable)
Network: cardano
Contract Address(es): N/A
Demo URL: N/A
Checklist
Code Quality
Testing
Additional Context
Related Issues
Fixes #
Screenshots/Demo (if applicable)
Notes for Reviewers
Thank you for contributing to Pyth Examples!