pnp-markets-solana

📁 pnp-protocol/solana-skill 📅 9 days ago
8
总安装量
4
周安装量
#34363
全站排名
安装命令
npx skills add https://github.com/pnp-protocol/solana-skill --skill pnp-markets-solana

Agent 安装分布

openclaw 4
windsurf 1
opencode 1
cursor 1
claude-code 1
antigravity 1

Skill 文档

PNP Markets (Solana)

Create and manage prediction markets on Solana Mainnet with any SPL token collateral. Optimized for high-throughput, low-latency, and permissionless operation.

When to Use This Skill

Use this skill when the user wants to:

  • Create prediction markets on Solana (V2 AMM or P2P)
  • Trade on markets (buy/sell YES/NO outcome tokens)
  • Settle markets as an oracle after the trading period ends
  • Redeem winning positions after settlement
  • Create social media markets (Twitter engagement, YouTube views)
  • Use custom tokens as prediction market collateral
  • Build info finance infrastructure with autonomous market resolution

Prerequisites

Before running any scripts, ensure you have:

  1. Solana Wallet: Base58-encoded private key with SOL for fees (~0.05 SOL minimum)
  2. USDC Tokens: Mainnet USDC (EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v) for market liquidity
  3. RPC Endpoint: Mainnet RPC URL (public or dedicated like Helius/QuickNode)

[!CAUTION] MAINNET ONLY: All scripts are configured for Solana Mainnet. Never use devnet. RPC defaults to https://api.mainnet-beta.solana.com.

[!IMPORTANT] ALWAYS USE USDC: All markets must be created with USDC as collateral. Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v (6 decimals).

# Install dependencies
cd scripts && npm install

# Set environment variables
export PRIVATE_KEY=<base58_private_key>
export RPC_URL=https://api.mainnet-beta.solana.com  # or dedicated RPC

Market Creation Flow

Solana prediction markets support two primary architectures:

  1. V2 AMM Markets (Default): Use Automated Market Makers with virtual liquidity. Best for publicly traded markets.
  2. P2P Markets: Direct peer-to-peer betting where the creator takes one side. Best for private or specific bets.

Standard V2 AMM Market Creation

Function: client.market.createMarket(params)

Creates a standard prediction market using PNP’s global oracle for resolution.

import 'dotenv/config';
import { PublicKey } from '@solana/web3.js';
import { PNPClient } from 'pnp-sdk';

const RPC_URL = process.env.RPC_URL || 'https://api.mainnet-beta.solana.com';
const PRIVATE_KEY = process.env.PRIVATE_KEY || '';

async function main() {
  const secretKey = PNPClient.parseSecretKey(PRIVATE_KEY);
  const client = new PNPClient(RPC_URL, secretKey);

  const result = await client.market.createMarket({
    question: 'Will Bitcoin reach $100K by end of 2025?',
    initialLiquidity: 1_000_000n,  // 1 USDC (6 decimals)
    endTime: BigInt(Math.floor(Date.now() / 1000) + 30 * 24 * 60 * 60),
    baseMint: new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'),
  });

  console.log('Market Address:', result.market.toBase58());
}

main().catch(console.error);

Parameters:

Parameter Type Required Description
question string ✅ The prediction question
initialLiquidity bigint ✅ Initial liquidity in raw units
endTime bigint ✅ Unix timestamp when trading ends
baseMint PublicKey ✅ Collateral token mint address

Returns: { signature: string, market: PublicKey }


Custom Oracle Markets (For AI Agents)

Custom oracles let AI agents become their own market resolvers—bypassing PNP’s global oracle entirely. This is the key primitive for autonomous agent-driven prediction markets.

Why Custom Oracles?

Use Case Description
AI Agents Build autonomous agents that create and resolve markets based on real-world data feeds
Private Forecasting Run internal prediction markets with proprietary resolution logic
Custom Data Sources Integrate any API—sports feeds, weather data, on-chain events, social metrics

Custom Oracle Functions

createMarketWithCustomOracle

When to use: Call this when you want your AI agent to have full control over market resolution. Your agent’s wallet becomes the oracle—only it can settle the market.

Why: Bypasses PNP’s global AI oracle. Essential for autonomous agents that need to resolve markets based on their own data sources or logic.

await client.createMarketWithCustomOracle({
  question: 'Will BTC hit $150K by Dec 2026?',
  initialLiquidity: 10_000_000n,  // 10 USDC (6 decimals)
  endTime: BigInt(Math.floor(Date.now() / 1000) + 30 * 24 * 60 * 60),
  collateralMint: USDC_MINT,
  settlerAddress: ORACLE_WALLET,  // Your agent's wallet
  yesOddsBps: 5000,  // Optional: 50/50 odds (range: 100-9900)
});

setMarketResolvable

When to use: Call this immediately after creating a custom oracle market—within 15 minutes.

Why: Markets are created in a frozen state. This activates trading. If not called within 15 minutes, the market is permanently untradeable.

await client.setMarketResolvable(marketAddress, true);

settleMarket

When to use: Call this after the market’s end time when you (as the oracle) know the outcome.

Why: Only the designated oracle can settle. This determines which side (YES/NO) wins and allows winners to redeem.

await client.settleMarket({
  market: marketAddress,
  yesWinner: true,  // false if NO wins
});

Critical: After market creation, you have a 15-minute buffer window to call setMarketResolvable(true). If not activated, the market is permanently frozen.


Social Media Markets

PNP provides native support for creating prediction markets linked to Twitter/X and YouTube content—ideal for social AI agents.

Twitter Markets

createMarketTwitter

When to use: Call this when creating a prediction market about tweet engagement (replies, likes, retweets, views).

Why: Automatically links the market to a specific tweet. PNP’s oracle can track tweet metrics for resolution.

await client.createMarketTwitter({
  question: 'Will this tweet cross 5000 replies?',
  tweetUrl: 'https://x.com/username/status/123456789',
  initialLiquidity: 1_000_000n,  // 1 USDC
  endTime: BigInt(Math.floor(Date.now() / 1000) + 30 * 24 * 60 * 60),
  collateralTokenMint: USDC_MINT,
});

Supported URL formats:

  • https://x.com/username/status/123456789
  • https://twitter.com/username/status/123456789

YouTube Markets

createMarketYoutube

When to use: Call this when creating a prediction market about video performance (views, likes, subscribers).

Why: Automatically links the market to a specific YouTube video. PNP’s oracle can track video metrics for resolution.

await client.createMarketYoutube({
  question: 'Will this video cross 1B views?',
  youtubeUrl: 'https://youtu.be/VIDEO_ID',
  initialLiquidity: 1_000_000n,  // 1 USDC
  endTime: BigInt(Math.floor(Date.now() / 1000) + 30 * 24 * 60 * 60),
  collateralTokenMint: USDC_MINT,
});

Supported URL formats:

  • https://youtu.be/VIDEO_ID
  • https://youtube.com/watch?v=VIDEO_ID
  • https://www.youtube.com/watch?v=VIDEO_ID

Social Market Use Cases

  • Engagement Prediction: Bet on whether a tweet will go viral
  • Content Performance: Predict video view counts
  • Influencer Markets: Create markets around creator milestones
  • Trend Detection: Use market prices as signals for trending content

Programmatic SDK Usage (For AI Agents)

This section provides the complete programmatic reference for AI agents to discover, analyze, trade, and settle markets without running CLI commands.

1. Client Initialization

import { PNPClient } from 'pnp-sdk';
import { PublicKey } from '@solana/web3.js';

// Initialize with RPC and Private Key (Uint8Array or Base58)
const client = new PNPClient(
  'https://api.mainnet-beta.solana.com',
  Uint8Array.from([/* 64-byte array */]) 
);

> [!TIP]
> Use `const secretKey = PNPClient.parseSecretKey(process.env.PRIVATE_KEY)` to automatically handle both Base58 strings and Uint8Array formats.

2. Market Discovery

AI agents should use these methods to find active markets before trading or analyzing.

Method Purpose
client.fetchMarketAddresses() Returns an array of V2 AMM market addresses from the proxy server.
client.fetchV3MarketAddresses() Returns an array of P2P (V3) market addresses.
client.fetchMarkets() Fetches on-chain data for all markets (higher latency, use sparingly).
client.fetchMarket(pubkey) Fetches detailed on-chain data for a specific market.
// Example: Find all P2P markets
const p2pAddresses = await client.fetchV3MarketAddresses();
console.log(`Found ${p2pAddresses.length} P2P markets.`);

3. Market Intelligence & Data Fetching

AI agents need comprehensive market data for decision-making. Use these SDK methods for analysis.

Price & Multiplier Analysis (Read-Only)

Function: client.getMarketPriceV2(marketAddress)

Returns real-time AMM pricing data with implied probabilities and payout multipliers.

const priceData = await client.getMarketPriceV2(marketAddress);

// Response structure:
// {
//   yesPrice: 0.65,           // YES token price (0-1 range)
//   noPrice: 0.35,            // NO token price (0-1 range)
//   yesMultiplier: 1.54,      // Payout ratio if YES wins
//   noMultiplier: 2.85,       // Payout ratio if NO wins
//   marketReserves: 1000.0,   // Total USDC locked
//   yesTokenSupply: 650.0,    // YES tokens minted
//   noTokenSupply: 350.0      // NO tokens minted
// }

// Calculate implied probabilities
const yesProbability = priceData.yesPrice * 100; // e.g., 65%
const noProbability = priceData.noPrice * 100;   // e.g., 35%

// Calculate potential returns
const betAmount = 10; // $10 USDC
const yesProfit = betAmount * (priceData.yesMultiplier - 1); // e.g., $5.40
const noProfit = betAmount * (priceData.noMultiplier - 1);   // e.g., $18.50

Comprehensive Market Data Analysis

Utility: Use the market-data.ts pattern to fetch complete market intelligence:

async function getComprehensiveMarketInfo(marketId: string) {
  const client = new PNPClient(RPC_URL); // Read-only
  
  // 1. Fetch on-chain market account data
  const { account: marketData } = await client.fetchMarket(new PublicKey(marketId));
  
  // Market account fields:
  // - question: string
  // - creator: PublicKey
  // - resolvable: boolean (can it be settled?)
  // - resolved: boolean (is it settled?)
  // - end_time: bigint (Unix timestamp)
  // - winning_token_id: 'yes' | 'no' | null
  // - yes_token_mint: PublicKey
  // - no_token_mint: PublicKey
  // - collateral_token: PublicKey
  
  // 2. Fetch settlement criteria from proxy server
  const criteria = await client.fetchSettlementCriteria(marketId);
  
  // Settlement criteria fields:
  // - resolvable: boolean
  // - winning_token_id: 'yes' | 'no' | undefined
  // - reasoning: string (AI explanation)
  // - category: string (e.g., 'coin-predictions')
  // - resolution_plan: Array<{step, action, fallback}>
  // - resolution_sources: string[] (API endpoints)
  
  // 3. Fetch settlement decision from proxy
  const settlementData = await client.fetchSettlementData(marketId);
  
  // Settlement data fields:
  // - answer: 'YES' | 'NO'
  // - reasoning: string (detailed explanation)
  
  // 4. Determine market state
  let state: string;
  if (marketData.resolved) {
    state = 'RESOLVED';
  } else if (!marketData.resolvable) {
    state = 'NOT RESOLVABLE';
  } else if (Date.now() > Number(marketData.end_time) * 1000) {
    state = 'ENDED (pending resolution)';
  } else {
    state = 'ACTIVE';
  }
  
  return { marketData, criteria, settlementData, state };
}

Key SDK Functions for Data Fetching:

Function Returns Use Case
client.fetchMarket(pubkey) Market account data Get on-chain market state
client.getMarketPriceV2(address) Price & multiplier data Calculate trading outcomes
client.fetchSettlementCriteria(address) AI resolution criteria Check if market can be settled
client.fetchSettlementData(address) AI settlement decision Get suggested outcome
client.trading.getMarketInfo(pubkey) Extended market info Get detailed trading data

4. Market Creation Lifecycle

Markets must follow a strict lifecycle, especially Custom Oracle markets.

V2 / Social Markets

  • client.createMarketTwitter(params)
  • client.createMarketYoutube(params)
  • client.market.createMarket(params) (Standard V2)

Custom Oracle (Agent-Controlled)

Step 1: Create

const result = await client.createMarketWithCustomOracle({
  question: '...',
  initialLiquidity: 10_000_000n,
  endTime: daysFromNow(7),
  collateralMint: USDC_MINT,
  settlerAddress: AGENT_WALLET, // You are the oracle
});

Step 2: Activate (Critical 15-Minute Buffer)

[!WARNING] You must call this within 15 minutes of creation, or the market is permanently frozen.

await client.setMarketResolvable(result.market, true);

P2P (V3) Markets

  • client.createP2PMarketGeneral(params)
  • client.createP2PMarketTwitter(params)
  • client.createP2PMarketYoutube(params)

5. Trading Operations

AI agents can execute trades programmatically on both AMM and P2P markets.

V2 AMM Trading Functions

Buy Tokens: client.trading.buyTokensUsdc(params)

Purchases YES or NO outcome tokens using USDC collateral. AMM automatically calculates token price.

const result = await client.trading.buyTokensUsdc({
  market: marketPubkey,
  buyYesToken: true,  // true = YES, false = NO
  amountUsdc: 10,     // Amount in USDC units (not raw)
});

// Returns: { signature: string, tokensReceived?: bigint }
console.log('Transaction:', result.signature);

Sell Tokens (Method 1): client.trading.sellTokensUsdc(params)

const result = await client.trading.sellTokensUsdc({
  market: marketPubkey,
  sellYesToken: true,          // true = YES, false = NO
  tokenAmount: 5_000_000_000_000_000_000n,  // Raw units (18 decimals)
});

// Returns: { signature: string, usdcReceived?: bigint }

Sell Tokens (Method 2): client.trading.burnDecisionTokensDerived(params)

Lower-level burn function used by scripts. Converts raw token amount to collateral.

const amountRaw = BigInt(Math.floor(sellAmount * 1e18)); // 18 decimals

const result = await client.trading.burnDecisionTokensDerived({
  market: marketPubkey,
  amount: amountRaw,
  burnYesToken: true,  // true = YES, false = NO
});

// Returns: { signature: string }

P2P Trading Function

Trade P2P Market: client.tradeP2PMarket(params)

Takes the opposite position from the market creator. If creator bet YES, you bet NO (and vice versa).

const result = await client.tradeP2PMarket({
  market: marketPubkey,
  side: 'yes',         // 'yes' or 'no'
  amount: 1_000_000n,  // Raw collateral units (e.g., 1 USDC = 1_000_000)
});

// Returns: { signature: string, market: string }

Key Differences:

  • V2 AMM: Uses amountUsdc in human-readable units (e.g., 10 for 10 USDC)
  • P2P: Uses amount in raw base units (e.g., 1_000_000n for 1 USDC with 6 decimals)
  • Token Decimals: Outcome tokens use 18 decimals, collateral varies (USDC = 6, SOL = 9)

6. Settlement & Proxy Integration

Settlement Functions (Oracle-only operations)

Settle Market: client.settleMarket(params)

Resolves a market by declaring the winning outcome. Only callable by the designated oracle.

const result = await client.settleMarket({
  market: marketPubkey,
  yesWinner: true,  // true = YES wins, false = NO wins
});

// Returns: { signature: string }
console.log('Market settled:', result.signature);

Prerequisites:

  • Current time must be past market’s end_time
  • Oracle wallet must match the market’s designated oracle
  • For custom oracle markets: Must have called setMarketResolvable(true) beforehand

Automated Settlement Flow:

// Fetch AI-suggested resolution from proxy
const criteria = await client.getSettlementCriteria(marketPubkey);

if (criteria.resolvable) {
  await client.settleMarket({
    market: marketPubkey,
    yesWinner: criteria.winning_token_id === 'yes',
  });
}

Check Market Settlement Status:

const { account: market } = await client.fetchMarket(marketPubkey);

if (market.resolved) {
  console.log('Winner:', market.winning_token_id); // 'yes' | 'no' | null
} else {
  const now = Math.floor(Date.now() / 1000);
  const ended = now > Number(market.end_time);
  console.log('Can settle:', ended && market.resolvable);
}

7. Redemption & Refunds

Redeem Winnings: client.redeemPosition(marketPubkey)

Converts winning outcome tokens back to collateral after market settlement.

// First check if market is settled
const { account: market } = await client.fetchMarket(marketPubkey);

if (!market.resolved) {
  throw new Error('Market not settled yet');
}

// Redeem winning position
const result = await client.redeemPosition(marketPubkey);

// Returns: { signature: string }
console.log('Redeemed:', result.signature);

Claim Creator Refund:

For markets that can’t be resolved, creators can reclaim their initial liquidity.

V2/Custom Oracle Markets: client.claimMarketRefund(marketPubkey)

const result = await client.claimMarketRefund(marketPubkey);
// Returns: { signature: string }

P2P Markets: client.claimP2PMarketRefund(marketPubkey)

const result = await client.claimP2PMarketRefund(marketPubkey);
// Returns: { signature: string }

Refund Eligibility:

  • Market must be unresolvable (checked via proxy or on-chain flag)

  • Caller must be the market creator

  • For custom oracle markets: 15-minute buffer period must have expired without activation

  • V2/Custom Oracle: await client.claimMarketRefund(marketPubkey)

  • P2P Markets: await client.claimP2PMarketRefund(marketPubkey)


Core Data Types

AI agents should understand these response structures:

MarketAccount (MarketType)

  • account.resolved: boolean – Is trading over?
  • account.resolvable: boolean – Can it be settled now?
  • account.end_time: bigint – Unix timestamp.
  • account.winning_token_id: string (‘yes’|’no’|null) – The winner.

PriceData (MarketPriceV2Data)

  • yesPrice: number (0-1)
  • noPrice: number (0-1)
  • yesMultiplier: number (e.g., 2.0)
  • marketReserves: number (Total USDC locked)

Common Token Mints (Ready-to-Use)

const USDC_MINT = new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v');
const USDT_MINT = new PublicKey('Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB');
const WSOL_MINT = new PublicKey('So11111111111111111111111111111111111111112');

Helper Functions

// Liquidity (USDC 6 decimals)
const usdcToRaw = (amount: number) => BigInt(amount * 1_000_000);

// End Time
const daysFromNow = (days: number) => BigInt(Math.floor(Date.now() / 1000) + days * 86400);

Supported Collateral

Markets can be created with any SPL token. Pre-configured aliases:

Alias Mint Address Decimals
USDC EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v 6
SOL So11111111111111111111111111111111111111112 9
USDT Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB 6

Script Quick Reference

All scripts are located in scripts/ and use dotenv/config to load environment variables from .env.

Script Purpose Run Command
create-market.ts Create standard V2 AMM market tsx scripts/create-market.ts
create-market-x.ts Create Twitter/X engagement market tsx scripts/create-market-x.ts
create-market-yt.ts Create YouTube views market tsx scripts/create-market-yt.ts
create-market-p2p.ts Create P2P betting market tsx scripts/create-market-p2p.ts
create-market-custom.ts Create custom oracle market tsx scripts/create-market-custom.ts
market-data.ts Fetch market info & settlement data tsx scripts/market-data.ts
trade.ts Buy/sell outcome tokens tsx scripts/trade.ts --buy --market <addr> --outcome YES --amount 10
settle.ts Resolve market as oracle tsx scripts/settle.ts --market <addr> --outcome YES
redeem.ts Claim winnings after settlement tsx scripts/redeem.ts --market <addr>

Environment Setup

Create a .env file in the root directory:

PRIVATE_KEY="your_base58_private_key_here"
RPC_URL="https://api.mainnet-beta.solana.com"

[!IMPORTANT] All scripts require the PRIVATE_KEY environment variable. Use PNPClient.parseSecretKey() to handle both Base58 strings and Uint8Arrays.


Common Errors & Recovery

Error Cause Solution
0x1770 (InvalidLiquidity) Liquidity below minimum Use ≥1 USDC or ≥0.05 SOL
Insufficient funds for rent Not enough SOL for account creation Ensure ≥0.05 SOL in wallet
Blockhash not found Transaction expired before confirmation Retry; use faster RPC (Helius/QuickNode)
15-minute buffer expired Didn’t call setMarketResolvable in time Market is permanently frozen; create new one
TokenAccountNotFound Missing Associated Token Account SDK auto-creates, but ensure SOL for rent
Oracle mismatch Wrong wallet trying to settle Only designated oracle can settle
Market not ended Trying to settle before end time Wait until market’s endTime passes

SDK Quick Reference

Method When to Use
createMarketWithCustomOracle({...}) When your agent needs to be the oracle and control resolution
setMarketResolvable(market, true) Immediately after creating custom oracle market (within 15 min)
settleMarket({market, yesWinner}) After market ends, to declare the winner as oracle
createMarketTwitter({...}) When creating markets about tweet engagement
createMarketYoutube({...}) When creating markets about video performance
market.createMarket({...}) For standard V2 AMM markets (PNP oracle resolves)
createP2PMarketGeneral({...}) When taking a position on one side of the bet
trading.buyTokensUsdc({...}) To buy YES/NO outcome tokens
redeemPosition(market) After settlement, to convert winning tokens to collateral
getMarketPriceV2(market) To fetch current prices (read-only, no wallet needed)
fetchMarket(market) To get on-chain market data

Resources