🎯

pnp-markets-solana

🎯Skill

from pnp-protocol/solana-skill

VibeIndex|
What it does

Enables creating, trading, and settling permissionless prediction markets on Solana using SPL tokens for high-throughput, low-latency forecasting and market-based information discovery.

pnp-markets-solana

Installation

npxRun with npx
npx ts-node trade.ts --buy --market <address> --outcome YES --amount 10
npxRun with npx
npx ts-node trade.ts --info --market <address>
📖 Extracted from docs: pnp-protocol/solana-skill
3Installs
-
AddedFeb 4, 2026

Skill Details

SKILL.md

Create, trade, and settle permissionless prediction markets on Solana Mainnet with any SPL token collateral. Use when building prediction market infrastructure, running contests, crowdsourcing probability estimates, adding utility to tokens, creating social media markets (Twitter/YouTube), or tapping into true information finance via market-based forecasting.

Overview

# 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).

```bash

# Install dependencies

cd scripts && npm install

# Set environment variables

export 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.

```typescript

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.

```typescript

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.

```typescript

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.

```typescript

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.

```typescript

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.

```typescript

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

```typescript

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. |

```typescript

// 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.

```typescript

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:

```typescript

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

```typescript

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.

```typescript

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.

```typescript

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)

```typescript

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.

```typescript

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).

```typescript

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.

```typescript

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:

```typescript

// 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:

```typescript

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.

```typescript

// 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)

```typescript

const result = await client.claimMarketRefund(marketPubkey);

// Returns: { signature: string }

```

P2P Markets: client.claimP2PMarketRefund(marketPubkey)

```typescript

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)

```typescript

const USDC_MINT = new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v');

const USDT_MINT = new PublicKey('Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB');

const WSOL_MINT = new PublicKey('So11111111111111111111111111111111111111112');

```

Helper Functions

```typescript

// 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 --outcome YES --amount 10 |

| settle.ts | Resolve market as oracle | tsx scripts/settle.ts --market --outcome YES |

| redeem.ts | Claim winnings after settlement | tsx scripts/redeem.ts --market |

Environment Setup

Create a .env file in the root directory:

```bash

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

  • Mainnet Explorer: [Solscan](https://solscan.io)
  • PNP SDK Docs: [https://docs.pnp.exchange/pnp-sdk](https://docs.pnp.exchange/pnp-sdk)
  • SDK Documentation: [references/api-reference.md](references/api-reference.md)
  • Advanced Examples: [references/examples.md](references/examples.md)