This PR resolves four open issues across the smart contract and backend layers. Each fix is logically isolated to its relevant module with accompanying unit tests.
Problem: The place_bet function in the prediction_market smart contract did not validate that the bet amount was greater than zero, allowing zero-value bets to pass through and corrupt pool accounting.
Fix:
- Added an early
require!(amount > 0)guard at the top ofinternal_place_bet. - Added a post-transfer balance check to confirm the contract actually received the expected token amount before crediting the bet.
Files changed:
contracts/prediction_market/src/lib.rs
Problem: Admin actions (e.g. manually resolving a market) were not logged, making it impossible to audit who did what and when.
Fix:
- Added a
logAdminActionutility that inserts a row into theadmin_audit_logtable on every privileged action, recording:admin_wallet,action_type,target_id,target_type,payload,ip_address, andcreated_at. - Wired
logAdminActioninto thePOST /api/admin/markets/:id/resolveendpoint. - Added a new
GET /api/admin/audit-logendpoint that returns paginated audit records, filterable byactionType,startDate, andendDate.
Files changed:
backend/src/routes/admin.jsbackend/src/routes/tests/admin.test.js
Problem: The POST /api/bets endpoint accepted bets without verifying that the accompanying Stellar transaction actually occurred on-chain, opening up the possibility of fake bets.
Fix:
- Before inserting a bet, the endpoint now fetches the transaction from the Stellar Horizon API using the provided
transaction_hash. - Validates that
source_accountmatches the submittedwalletAddressand that the on-chainamountmatches the betamount. - Returns
400if the transaction is not found or does not match; returns400if the Stellar wallet address format is invalid. - Added Redis-backed deduplication to prevent replayed transaction hashes.
Files changed:
backend/src/routes/bets.jsbackend/src/routes/tests/bets.test.js
Problem: After a market was resolved, payouts were processed immediately with no opportunity for participants to dispute an incorrect outcome.
Fix:
- When a market is resolved, a
dispute_window_ends_attimestamp is now set (configurable, default 24 hours). - The
POST /api/markets/payout/:marketIdendpoint checks whether the dispute window is still open; if so it returns400 — Dispute window is still open. - Added a new
GET /api/markets/:marketId/dispute-statusendpoint that returns the current dispute window status and time remaining.
Files changed:
backend/src/routes/markets.jsbackend/src/routes/tests/markets.test.js
Unit tests were added for all backend changes. Each test file mocks database and third-party dependencies to run in isolation:
| File | Tests |
|---|---|
backend/src/routes/tests/admin.test.js |
Admin audit log insertion, audit log endpoint with filters |
backend/src/routes/tests/bets.test.js |
Invalid wallet rejection, transaction hash mismatch, valid bet acceptance |
backend/src/routes/tests/markets.test.js |
Dispute window set on resolution, payouts blocked during window, payouts allowed after window, dispute status endpoint |
Smart contract tests are in contracts/prediction_market/ and cover the zero-amount guard.
-- Issue #499: Admin audit trail
CREATE TABLE IF NOT EXISTS admin_audit_log (
id SERIAL PRIMARY KEY,
admin_wallet TEXT NOT NULL,
action_type TEXT NOT NULL,
target_id INTEGER,
target_type TEXT,
payload JSONB,
ip_address TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Issue #501: Dispute window column
ALTER TABLE markets
ADD COLUMN IF NOT EXISTS dispute_window_ends_at TIMESTAMPTZ;- Issue #487 — Zero-amount guard in smart contract
- Issue #499 — Admin audit trail with logging and retrieval endpoint
- Issue #500 — Stellar Horizon transaction verification on bet placement
- Issue #501 — Dispute window blocking premature payouts
- Unit tests added for all backend changes
- No breaking changes to existing API contracts
- DB migration SQL documented above