Fatecast

A decentralized Ethereum-based prediction market using AI agents, Pyth Oracle, and PYUSD.

Fatecast

Created At

ETHOnline 2025

Project Description

Fatecast is a decentralized social prediction market platform built on Ethereum that enables users to bet on future events using cryptocurrency. It’s a sophisticated Web3 application that combines smart contracts, AI automation, and real-world data feeds.

What It Does

At its core, Fatecast allows users to:

• Create prediction markets for YES/NO questions about future events (e.g., “Will BTC reach $100,000 by November 1, 2025?”)

• Place bets using PYUSD (PayPal USD stablecoin) on their predictions

• Automatically resolve events using real-world data from Pyth Oracle

• Claim winnings when their predictions are correct

Key Components

  1. Smart Contract (Solidity) - Handles bets, funds, and event resolution on Ethereum

  2. ASI Agent (TypeScript) - AI bot that automatically creates and resolves prediction events

  3. Frontend (Next.js) - Web interface for users to browse and bet

  4. Pyth Oracle - Provides real-world crypto price data for trustless resolution

  5. Farcaster Integration - Social sharing features

Use Cases

  1. Crypto Price Predictions: Bet on whether Bitcoin, Ethereum, or Solana will reach certain price targets

  2. Time-based Events: Create markets with specific deadlines for resolution

  3. Decentralized Decision Markets: Leverage crowd wisdom for predictions

  4. Social Betting: Share predictions via Farcaster with the community

How it's Made

Core Architecture

I. Smart Contract Layer (Solidity) The heart of Fatecast is the PredictionMarket.sol contract built with Foundry and Solidity 0.8.20. Key technical decisions:

Technology Stack:

  • OpenZeppelin Contracts for battle-tested security primitives:
    • Ownable for access control
    • SafeERC20 for secure token transfers
    • ReentrancyGuard to prevent reentrancy attacks
    • Pausable for emergency circuit breaker functionality

Contract Design Pattern:

struct Event {
    uint256 id;
    string question;
    bytes32 pythFeedId;        // Links to Pyth oracle
    int64 targetPrice;          // Price threshold for resolution
    uint256 deadline;
    uint256 totalYes;           // Pool tracking
    uint256 totalNo;
    uint256 totalPool;
    bool resolved;
    bool outcome;
    address creator;
    uint256 createdAt;
}

Clever Implementation Details:

  1. Dual Authorization Pattern: The contract uses onlyOwnerOrAgent modifier, allowing both the contract owner and an authorized ASI agent to create events - enabling trustless automation while maintaining emergency admin control.

  2. Oracle Integration: The _getPythPrice() function uses low-level staticcall to fetch prices from Pyth, with manual assembly for data extraction:

(bool success, bytes memory data) = pythOracle.staticcall(
    abi.encodeWithSignature("getPrice(bytes32)", feedId)
);
// Assembly parsing for MockPyth compatibility
assembly {
    price := mload(add(data, 32))
}
  1. Active Event Tracking: Uses a dynamic array activeEventIds[] that's pruned on resolution to optimize gas for event queries - a pragmatic approach to on-chain data management.

  2. Proportional Payout System: Winners receive (userBet / winningPool) * totalPool, ensuring fair distribution without requiring complex AMM mathematics.

II. ASI Agent Backend (TypeScript/Node.js) The automation layer is built with TypeScript and ethers.js v6, featuring a sophisticated event lifecycle manager:

Architecture Pattern:

asi-agent/
├── src/
│   ├── index.ts           # Main orchestrator with cron scheduling
│   ├── eventCreator.ts    # Event generation logic
│   ├── eventResolver.ts   # Resolution monitoring
│   └── utils/
│       ├── contracts.ts   # Contract instances & ABIs
│       ├── pyth.ts       # Pyth API integration
│       └── logger.ts     # Winston logging setup

Notable Technical Implementations:

  1. Pyth Hermes API Integration: Instead of on-chain price updates (expensive), the agent uses Pyth's REST API:
export async function fetchPythPrice(feedId: string): Promise<PythPrice | null> {
  const url = `${config.pythHermesUrl}/api/latest_price_feeds?ids[]=${feedId}`;
  const response = await axios.get<PythPrice[]>(url);
  // Price formatting with exponential notation
  const price = parseInt(priceData.price.price);
  const expo = priceData.price.expo;
  return price * Math.pow(10, expo);
}
  1. Retry Logic with Exponential Backoff: Robust error handling for oracle price fetches:
for (let i = 0; i < maxRetries; i++) {
  try {
    const priceData = await fetchPythPrice(feedId);
    if (priceData) return formatPythPrice(priceData);
  } catch (error) {
    await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
  }
}
  1. Continuous Monitoring with Cron: Event creation every hour, resolution checks every 5 minutes:
cron.schedule(cronSchedule, async () => {
  await createPredictionEvent();
});
startResolutionMonitor(config.eventResolutionInterval);
  1. Daily Rate Limiting: Prevents spam with in-memory counter reset:
let eventsCreatedToday = 0;
let lastResetDate = new Date().toDateString();
// Resets automatically on date change
Frontend (Next.js 16 + React 19)

III. Modern React stack with Web3 integration:

Integration Highlights:

  • RainbowKit for wallet connection UX
  • Wagmi hooks for contract interactions
  • Farcaster MiniApp SDK for social prediction sharing
  • Blockscout SDK for contract verification links

Particularly Hacky/Notable Solutions

  1. Mock vs. Real Pyth Oracle Compatibility The contract's _getPythPrice() function uses assembly to handle both MockPyth (for testing) and real Pyth oracle responses - a pragmatic hack avoiding complex interface implementations.

  2. Int64 Price Conversion Pyth uses int64 with 8 decimal precision, requiring careful conversion:

export function toPythInt64(price: number): bigint {
  return BigInt(Math.floor(price * 1e8));
}
  1. Event Array Pruning Rather than using mappings exclusively, the contract maintains an array of active events and manually removes resolved ones - trading gas on resolution for cheaper queries:
function _removeFromActiveEvents(uint256 eventId) internal {
    for (uint256 i = 0; i < activeEventIds.length; i++) {
        if (activeEventIds[i] == eventId) {
            activeEventIds[i] = activeEventIds[activeEventIds.length - 1];
            activeEventIds.pop();
            break;
        }
    }
}
  1. PYUSD Integration on Sepolia Uses PayPal's PYUSD stablecoin on Sepolia testnet (0x...) with ERC20 standard interface - innovative choice for a stablecoin prediction market.

  2. Deployment Artifacts as Configuration The agent reads deployment addresses from JSON files rather than hardcoding:

const deploymentFile = vm.readFile("deployments/deployment-11155111.json");
const marketAddress = vm.parseJsonAddress(deploymentFile, ".contracts.PredictionMarket");
background image mobile

Join the mailing list

Get the latest news and updates