Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Create, trade, and settle permissionless prediction markets on Solana. Use when building prediction market infrastructure, creating social media markets (Twitter/YouTube/DeFiLlama), setting up custom oracle resolution, P2P betting, or autonomous agent-driven forecasting. Supports V2 AMM, P2P (V3), and custom oracle markets with any SPL token collateral (including Token-2022).
.claude/skills/internet-court-pnp-markets-solana/SKILL.md| Model | Eval pass | Runs |
|---|---|---|
| gemini-3.6-flash | 100% | 12 |
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 220% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 146% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 225% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 312% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 287% | 0% |
Create and manage prediction markets on Solana with any SPL token collateral. Supports V2 AMM markets, P2P direct bets, custom oracle resolution for AI agents, and social media markets (Twitter, YouTube, DeFiLlama).
Use when the user wants to:
Triggers: prediction market, betting, oracle, settlement, forecast, YES/NO, outcome token, market resolution, P2P bet, custom oracle, social media market, autonomous market, info finance, market creation, prediction, wager, binary outcome
Do not use when:
| Item | Address | Notes | |------|---------|-------| | PNP Program (Mainnet) | 8PyE2dizL52ga7ytqLtqRyjwWp4yXEx8M5Z4BAHgHuTb | Main prediction market program | | PNP Program (Devnet) | pnpkv2qnh4bfpGvTugGDSEhvZC7DP4pVxTuDykV3BGz | Devnet testing program | | USDC Mint | EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v | Common collateral (6 decimals) | | USDT Mint | Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB | Alternative stable (6 decimals) | | WSOL Mint | So11111111111111111111111111111111111111112 | Wrapped SOL (9 decimals) |
| Token Type | Decimals | Example | |-----------|----------|---------| | USDC / USDT | 6 | 1 USDC = 1_000_000n | | SOL (wrapped) | 9 | 1 SOL = 1_000_000_000n | | Decision tokens (YES/NO) | 6 | Minted per-market by the program |
typescript// Conversion helpers const usdcToRaw = (amount: number) => BigInt(Math.floor(amount * 1_000_000)); const daysFromNow = (days: number) => BigInt(Math.floor(Date.now() / 1000) + days * 86400); // Example const liquidity = usdcToRaw(100); // 100 USDC -> 100_000_000n const endTime = daysFromNow(7); // 7 days from now -> Unix timestamp as bigint
> !IMPORTANT] > Collateral can be any SPL token or Token-2022 token. Pass the token's mint address as baseMint or collateralTokenMint. Make sure to use the correct decimals for the chosen token (e.g., USDC/USDT = 6, SOL = 9). Common mints are listed in the table above for reference.
bash# 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
typescriptimport { PNPClient } from 'pnp-sdk'; import { PublicKey } from '@solana/web3.js'; const client = new PNPClient( process.env.RPC_URL || 'https://api.mainnet-beta.solana.com', process.env.PRIVATE_KEY! // Base58 string or Uint8Array ); // Collateral can be any SPL token (including Token-2022) — use the mint address of your chosen token const USDC = new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'); // Create a prediction market const result = await client.market.createMarket({ question: 'Will Bitcoin reach $100K by end of 2025?', initialLiquidity: 1_000_000n, // 1 USDC (6 decimals) — adjust decimals for your collateral token endTime: BigInt(Math.floor(Date.now() / 1000) + 30 * 24 * 60 * 60), baseMint: USDC, // Any SPL or Token-2022 mint }); console.log('Market created:', result.market.toBase58()); // Returns: { signature: string, market: PublicKey }
> !TIP] > Use PNPClient.parseSecretKey(process.env.PRIVATE_KEY) to handle both Base58 strings and JSON array formats automatically.
Markets follow a strict state progression:
CREATED ──────► ACTIVE ──────► ENDED ──────► RESOLVED ──────► CLAIMED
│ │ │ │ │
│ Trading live No new trades Oracle declares Winners redeem
│ Users buy/sell allowed YES/NO winner collateral
│ YES/NO tokens
│
└── initialLiquidity locked, PNP global oracle resolvesCREATED ──► [15-MIN BUFFER] ──► ACTIVE ──► ENDED ──► RESOLVED ──► CLAIMED
│ │ │ │ │ │
│ setMarketResolvable(true) Trade Wait for settleMarket() redeem
│ MUST call within 15 min! live endTime (oracle only)
│
└── Market starts frozen. If not activated within 15 minutes,
it is PERMANENTLY FROZEN and cannot be recovered.| From | To | Method | Condition | |------|-----|--------|-----------| | CREATED | ACTIVE | setMarketResolvable(market, true) | Custom oracle only; must be within 15 min of creation | | ACTIVE | ENDED | (automatic) | Unix timestamp reaches endTime | | ENDED | RESOLVED | settleMarket({market, yesWinner}) | Oracle-only; can only be called after endTime | | RESOLVED | CLAIMED | redeemPosition(market) | Any winner; available forever after resolution |
These operations fetch data without executing transactions. Initialize with just an RPC URL:
typescriptconst readOnlyClient = new PNPClient('https://api.mainnet-beta.solana.com');
fetchMarketAddresses() — Discover V2 Marketstypescriptconst addresses: string[] = await client.fetchMarketAddresses(); console.log(`Found ${addresses.length} V2 AMM markets`); // Returns: string[] of market public key base58 addresses
fetchMarket(pubkey) — Get On-Chain Market Datatypescriptconst { account } = await client.fetchMarket(new PublicKey('HxnpHygK1v7TqodWqAv6RvcEiK9zxAgw5jPZ6rskgj2E')); // MarketType fields: // account.question: string // account.creator: PubkeyLike // account.resolvable: boolean // account.resolved: boolean // account.end_time: U64Like (Unix timestamp) // account.winning_token_id: 'yes' | 'no' | 'none' | null // account.yes_token_mint: PubkeyLike // account.no_token_mint: PubkeyLike // account.collateral_token: PubkeyLike // account.market_reserves: U64Like // account.initial_liquidity: U64Like
getMarketPriceV2(market) — Get Current Prices & Multiplierstypescriptconst priceData = await client.getMarketPriceV2(marketAddress); console.log({ yesPrice: priceData.yesPrice, // 0-1 range (e.g., 0.65 = 65%) noPrice: priceData.noPrice, // 0-1 range (e.g., 0.35 = 35%) yesMultiplier: priceData.yesMultiplier, // Payout ratio if YES wins (e.g., 1.54x) noMultiplier: priceData.noMultiplier, // Payout ratio if NO wins (e.g., 2.85x) marketReserves: priceData.marketReserves, // Total collateral locked (UI units) yesTokenSupply: priceData.yesTokenSupply, // YES tokens minted (UI units) noTokenSupply: priceData.noTokenSupply, // NO tokens minted (UI units) }); // AMM Price Formulas: // yesPrice = (marketReserves * yesTokenSupply) / (yesTokenSupply^2 + noTokenSupply^2) // noPrice = (marketReserves * noTokenSupply) / (yesTokenSupply^2 + noTokenSupply^2) // yesMultiplier = 1 + (noTokenSupply / yesTokenSupply)^2 // noMultiplier = 1 + (yesTokenSupply / noTokenSupply)^2
fetchSettlementCriteria(market) — Get AI Resolution Infotypescriptconst criteria = await client.fetchSettlementCriteria(marketAddress); // Returns: { category, reasoning, resolvable, resolution_sources, settlement_criteria }
fetchSettlementData(market) — Get Settlement Decisiontypescriptconst data = await client.fetchSettlementData(marketAddress); // Returns: { answer: 'YES'|'NO', reasoning: string }
trading.getMarketInfo(pubkey) — Extended Market Infotypescriptconst info = await client.trading.getMarketInfo(new PublicKey(marketAddress)); // Returns: { address, question, id, creator, initialLiquidity, marketReserves, // endTime, resolvable, resolved, winningTokenId, // yesTokenMint, noTokenMint, collateralToken, // yesTokenSupplyMinted, noTokenSupplyMinted }
market.createMarket(params)Creates a V2 AMM market using PNP's global oracle for resolution.
typescriptconst result = await client.market.createMarket({ question: 'Will SOL hit $250 by end of March?', initialLiquidity: 10_000_000n, // 10 USDC (6 decimals) endTime: BigInt(Math.floor(Date.now() / 1000) + 7 * 86400), // 7 days baseMint: new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'), }); console.log('Market:', result.market.toBase58()); // Returns: { signature: string, market: PublicKey }
| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | question | string | Yes | The prediction question | | initialLiquidity | bigint | Yes | Initial liquidity in raw units (decimals depend on collateral token) | | endTime | bigint | Yes | Unix timestamp when trading ends | | baseMint | PublicKey | No | Collateral token mint — any SPL or Token-2022 token |
createMarketWithCustomOracle(params)When to use: When your AI agent needs full control over market resolution. Your agent's wallet becomes the oracle.
typescriptconst result = 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 * 86400), collateralMint: new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'), settlerAddress: AGENT_WALLET_PUBKEY, // Your agent's wallet is the oracle yesOddsBps: 5000, // Optional: 50/50 odds (range: 100-9900) }); // Returns: { market: PublicKey, signature: string }
| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | question | string | Yes | The prediction question | | initialLiquidity | bigint | Yes | Initial liquidity in raw units | | endTime | bigint | Yes | Unix timestamp when trading ends | | collateralMint | PublicKey | Yes | Collateral token mint | | settlerAddress | PublicKey | Yes | Oracle wallet that will settle the market | | yesOddsBps | number | No | Initial YES odds in basis points (100-9900, default 5000) |
> !WARNING] > After creation, you must call setMarketResolvable(market, true) within 15 minutes or the market is permanently frozen.
createMarketV2WithCustomOdds(params)typescriptconst result = await client.createMarketV2WithCustomOdds({ question: 'Will Ethereum flip Bitcoin by 2026?', initialLiquidity: 50_000_000n, // 50 USDC endTime: daysFromNow(90), collateralTokenMint: USDC, yesOddsBps: 2000, // 20% YES / 80% NO oracle: CUSTOM_ORACLE_PUBKEY, // Optional: custom oracle }); // Returns: { signature: string, market: string }
createMarketTwitter(params)typescriptconst result = await client.createMarketTwitter({ question: 'Will this tweet cross 5000 replies?', tweetUrl: 'https://x.com/username/status/123456789', initialLiquidity: 1_000_000n, // 1 USDC endTime: daysFromNow(7), collateralTokenMint: USDC, }); // Returns: { signature: string, market: string, detectedTwitterUrl?, isTweetIdFormat? }
Supported URL formats: https://x.com/user/status/ID, https://twitter.com/user/status/ID
createMarketYoutube(params)typescriptconst result = await client.createMarketYoutube({ question: 'Will this video cross 1B views?', youtubeUrl: 'https://youtu.be/VIDEO_ID', initialLiquidity: 1_000_000n, endTime: daysFromNow(30), collateralTokenMint: USDC, }); // Returns: { signature: string, market: string, detectedYoutubeUrl? }
Supported: https://youtu.be/ID, https://youtube.com/watch?v=ID, https://youtube.com/shorts/ID
createMarketDefiLlama(params)typescriptconst result = await client.createMarketDefiLlama({ question: 'Will Uniswap TVL exceed $5B this quarter?', protocolName: 'uniswap-v3', metric: 'tvl', initialLiquidity: 5_000_000n, endTime: daysFromNow(30), collateralTokenMint: USDC, }); // Returns: { signature: string, market: string, detectedProtocol?, detectedMetric? }
createP2PMarketGeneral(params)Direct peer-to-peer betting where the creator takes one side.
typescriptconst result = await client.createP2PMarketGeneral({ question: 'Will our DAO pass Proposal #42?', initialAmount: 100_000_000n, // 100 USDC (raw units, 6 decimals) side: 'yes', // Creator bets YES creatorSideCap: 500_000_000n, // Max 500 USDC on creator's side endTime: daysFromNow(14), collateralTokenMint: USDC, }); // Returns: { signature: string, market: PubkeyLike, yesTokenMint: PubkeyLike, noTokenMint: PubkeyLike }
createP2PMarketSimple(params)UI-friendly wrapper that handles USDC conversion automatically:
typescriptconst result = await client.createP2PMarketSimple({ question: 'Will it rain tomorrow?', side: 'yes', amountUsdc: 50, // 50 USDC (human-readable, SDK converts) daysUntilEnd: 1, // Defaults to 30 days creatorSideCapMultiplier: 5, // 5x initial = 250 USDC cap (default) });
createP2PMarketTwitter(params) — P2P + Twitter URL detectioncreateP2PMarketYoutube(params) — P2P + YouTube URL detectioncreateP2PMarketDefiLlama(params) — P2P + DeFiLlama metricscreateP2PMarketTwitterSimple(params) — UI-friendly P2P TwittercreateP2PMarketYoutubeSimple(params) — UI-friendly P2P YouTubecreateP2PMarketDefiLlamaSimple(params) — UI-friendly P2P DeFiLlamacreateMarketP2PWithCustomOdds(params) — P2P with custom initial oddstrading.buyTokensUsdc(params)Purchases YES or NO outcome tokens using USDC collateral.
typescriptconst result = await client.trading.buyTokensUsdc({ market: marketPubkey, buyYesToken: true, // true = YES, false = NO amountUsdc: 10, // 10 USDC (human-readable, SDK converts using mint decimals) minimumOut: 0n, // Optional: minimum tokens received (slippage protection) }); // Returns: // { signature: string, usdcSpent: number, tokensReceived: number, // before: BalanceSnapshot, after: BalanceSnapshot }
trading.sellTokensBase(params)typescriptconst result = await client.trading.sellTokensBase({ market: marketPubkey, burnYesToken: true, // true = sell YES, false = sell NO amountBaseUnits: 5_000_000n, // Raw token units (6 decimals) }); // Returns: { signature: string, tokensSold: number, usdcReceived: number }
buyV3TokensUsdc(params)typescriptconst result = await client.buyV3TokensUsdc({ market: marketPubkey, buyYesToken: true, amountUsdc: 10, // Human-readable USDC }); // Returns: { signature: string }
Key Differences:
trading.buyTokensUsdc() — human-readable amountUsdcbuyV3TokensUsdc() — human-readable amountUsdctrading.sellTokensBase() — raw units amountBaseUnitssetMarketResolvable(market, resolvable, forceResolve?)When to use: Immediately after creating a custom oracle market. Activates trading.
typescriptawait client.setMarketResolvable(marketAddress, true); // Signature: setMarketResolvable(market: PublicKey | string, resolvable: boolean, forceResolve?: boolean) // Returns: { signature: string }
> !CAUTION] > Must be called within 15 minutes of creation. After that, the market is permanently frozen and cannot be recovered. Create a new market instead.
For P2P markets: setMarketResolvableP2p(market, resolvable)
After endTime passes, use the proxy server to get the AI-suggested resolution, then settle:
typescript// 1. Wait for settlement criteria (retries every 2s for up to 15 min) const { resolvable, answer, criteria } = await client.waitForSettlementCriteria(marketAddress); // 2. Settle the market if resolvable if (resolvable) { // Use the anchorMarket module for V2 settlement const result = await client.anchorMarket.settleMarket({ market: new PublicKey(marketAddress), yesWinner: answer === 'YES', }); console.log('Settled:', result.signature); }
redeemPosition(market)Converts winning outcome tokens back to collateral after market settlement.
typescriptconst { account: market } = await client.fetchMarket(marketPubkey); if (!market.resolved) throw new Error('Market not settled yet'); const result = await client.redeemPosition(marketPubkey); // Returns: { signature: string }
redeemP2PPosition(market)typescriptconst result = await client.redeemP2PPosition(marketPubkey); // Returns: { signature: string }
| Error | Cause | Solution | |-------|-------|----------| | 0x1770 (InvalidLiquidity) | Liquidity below minimum | Use >= 1 USDC (1_000_000n) | | 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 ATAs, 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 | | Market not tradable: ended | Trying to trade after endTime | Market has ended; no more trades allowed | | Insufficient collateral balance | Wallet balance below trade amount | Ensure wallet has enough USDC | | VALIDATION_ERROR | Invalid parameters | Check input types and ranges | | TRANSPORT_ERROR | Network/RPC failure | Retry with exponential backoff | | PROGRAM_ERROR | On-chain program error | Check .programErrorCode and .logs for details |
The SDK uses typed errors (SdkError, ValidationError, TransportError, ProgramError):
typescriptimport { SdkError, ValidationError, TransportError, ProgramError } from 'pnp-sdk'; async function safeMarketOperation<T>( operation: () => Promise<T>, operationName: string ): Promise<{ ok: boolean; result?: T; error?: { code: string; message: string; retryable: boolean } }> { try { const result = await operation(); return { ok: true, result }; } catch (error: any) { console.error(`${operationName} failed:`, error.message); if (error instanceof TransportError || error.message?.includes('Blockhash')) { return { ok: false, error: { code: 'TRANSPORT_ERROR', message: 'Network error. Retry with backoff.', retryable: true }, }; } if (error instanceof ProgramError) { return { ok: false, error: { code: `PROGRAM_ERROR_${error.programErrorCode ?? 'UNKNOWN'}`, message: error.message, retryable: false, }, }; } if (error instanceof ValidationError) { return { ok: false, error: { code: 'VALIDATION_ERROR', message: error.message, retryable: false }, }; } if (error.message?.includes('Market not tradable')) { return { ok: false, error: { code: 'MARKET_ENDED', message: error.message, retryable: false }, }; } return { ok: false, error: { code: 'UNKNOWN', message: error.message || String(error), retryable: false }, }; } } // Usage const result = await safeMarketOperation( () => client.market.createMarket(params), 'Create Market' ); if (!result.ok && result.error?.retryable) { // Retry with exponential backoff }
PNP operations use these approximate CU limits internally:
| Operation | CU Limit | |-----------|----------| | Market creation (V2) | 400,000 | | Trading (mint/burn) | 600,000 | | Settlement | 800,000 | | Set market resolvable | 800,000 |
The SDK sets these automatically. For custom transactions, set explicit budgets:
typescriptimport { ComputeBudgetProgram, Transaction } from '@solana/web3.js'; const tx = new Transaction(); tx.add( ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000 }), ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 50_000 }) );
During network congestion, increase priority fees:
| Congestion | microLamports | |-----------|---------------| | Normal | 10,000 | | Moderate | 50,000 | | High | 100,000+ |
typescriptasync function executeWithRetry( action: () => Promise<string>, maxRetries = 3, timeoutMs = 30_000 ): Promise<string> { for (let attempt = 1; attempt <= maxRetries; attempt++) { try { return await Promise.race([ action(), new Promise<string>((_, reject) => setTimeout(() => reject(new Error('Timeout')), timeoutMs) ), ]); } catch (error: any) { if (attempt === maxRetries) throw error; const delay = Math.pow(2, attempt - 1) * 1000; console.log(`Retry ${attempt} after ${delay}ms...`); await new Promise(r => setTimeout(r, delay)); } } throw new Error('Max retries exceeded'); }
typescriptfunction validateMarketParams(params: { question: string; initialLiquidity: bigint; endTime: bigint }) { if (!params.question || params.question.length === 0) throw new Error('Question cannot be empty'); if (params.initialLiquidity < 1_000_000n) throw new Error('Min liquidity is 1 USDC (1_000_000n)'); if (params.endTime <= BigInt(Math.floor(Date.now() / 1000))) throw new Error('End time must be in the future'); }
typescriptconst RPC_URLS = [ process.env.RPC_URL || 'https://api.mainnet-beta.solana.com', 'https://mainnet.helius-rpc.com/?api-key=YOUR_KEY', 'https://solana-mainnet.g.alchemy.com/v2/YOUR_KEY', ];
| Method | Scope | When to Use | |--------|-------|-------------| | market.createMarket({...}) | Write | Standard V2 AMM markets | | createMarketWithCustomOracle({...}) | Write | Agent-controlled market resolution | | createMarketV2WithCustomOdds({...}) | Write | V2 with non-50/50 starting odds | | createP2PMarketGeneral({...}) | Write | P2P direct bets (raw units) | | createP2PMarketSimple({...}) | Write | P2P direct bets (UI-friendly) | | createMarketTwitter({...}) | Write | V2 markets linked to tweets | | createMarketYoutube({...}) | Write | V2 markets linked to videos | | createMarketDefiLlama({...}) | Write | V2 markets linked to DeFi metrics | | setMarketResolvable(market, true) | Write | Activate custom oracle markets (15 min!) | | setMarketResolvableP2p(market, true) | Write | Activate P2P custom oracle markets | | trading.buyTokensUsdc({...}) | Write | Buy YES/NO tokens on V2 markets | | trading.sellTokensBase({...}) | Write | Sell YES/NO tokens on V2 markets | | buyV3TokensUsdc({...}) | Write | Buy tokens on P2P/V3 markets | | redeemPosition(market) | Write | Redeem V2 winning position | | redeemP2PPosition(market) | Write | Redeem P2P winning position | | getMarketPriceV2(market) | Read | Current prices and multipliers | | fetchMarket(pubkey) | Read | On-chain market account data | | fetchMarketAddresses() | Read | List all V2 market addresses | | fetchMarkets() | Read | All V2 markets with full data | | fetchSettlementCriteria(market) | Read | AI resolution criteria | | fetchSettlementData(market) | Read | Settlement decision | | waitForSettlementCriteria(market) | Read | Retry until criteria available | | trading.getMarketInfo(pubkey) | Read | Extended market info | | trading.getBalances(market) | Read | User's token balances for market | | trading.getPrices(market) | Read | Token supply-based price shares | | PNPClient.parseSecretKey(str) | Static | Parse Base58 or JSON array private key | | client.uiToRaw(amount, decimals) | Util | Convert UI amount to raw bigint | | PNPClient.detectTwitterUrl(text) | Static | Extract Twitter URL from question | | PNPClient.detectYoutubeUrl(text) | Static | Extract YouTube URL from question | | PNPClient.detectDefiLlamaUrl(text) | Static | Extract DeFiLlama format from question |
All scripts are located in scripts/ and use dotenv/config to load .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> |
pnp-sdk on npmsolana-skill/
├── SKILL.md # Main entry point (this file)
│ ├── When to Use / Triggers
│ ├── Program IDs & Precision
│ ├── Quick Start
│ ├── Market Lifecycle / State Machine
│ ├── Core Operations (Read-Only, Creation, Trading, Settlement, Redemption)
│ ├── Error Handling
│ ├── Production Hardening
│ └── SDK Quick Reference
│
├── README.md # Human-facing overview for GitHub
│
├── references/
│ ├── api-reference.md # Complete method signatures & parameter docs
│ ├── types-and-precision.md # TypeScript interfaces, decimal constants
│ ├── program-addresses.md # Program IDs, PDA derivations, token mints
│ ├── examples.md # Advanced code patterns
│ └── use-cases.md # Real-world prediction market patterns
│
├── scripts/ # CLI utilities for testing
│ ├── create-market.ts
│ ├── create-market-custom.ts
│ ├── create-market-x.ts
│ ├── create-market-yt.ts
│ ├── create-market-p2p.ts
│ ├── market-data.ts
│ ├── trade.ts
│ ├── settle.ts
│ └── redeem.ts
│
└── package.jsonProgressive disclosure: SKILL.md covers 80% of use cases. Reference files handle the deep details.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 21,494 | 16,602 | -23% | 1 | 1 | 0% | 3,542 | 11,337 | +220% | 0 | 0 | — |
case-02 | fail→pass | 28,131 | 16,723 | -41% | 1 | 1 | 0% | 4,601 | 11,313 | +146% | 0 | 0 | — |
case-03 | fail→pass | 22,637 | 18,383 | -19% | 1 | 1 | 0% | 3,621 | 11,751 | +225% | 0 | 0 | — |
case-04 | fail→pass | 18,732 | 13,239 | -29% | 1 | 1 | 0% | 2,539 | 10,461 | +312% | 0 | 0 | — |
case-05 | pass→pass | 21,584 | 12,092 | -44% | 1 | 1 | 0% | 3,191 | 10,077 | +216% | 0 | 0 | — |
case-06 | fail→pass | 19,373 | 15,178 | -22% | 1 | 1 | 0% | 2,780 | 10,752 | +287% | 0 | 0 | — |
case-07 | fail→pass | 16,219 | 12,087 | -25% | 1 | 1 | 0% | 1,984 | 10,184 | +413% | 0 | 0 | — |
case-08 | fail→pass | 26,609 | 15,766 | -41% | 1 | 1 | 0% | 3,883 | 10,990 | +183% | 0 | 0 | — |
case-09 | fail→pass | 17,256 | 14,584 | -15% | 1 | 1 | 0% | 2,430 | 10,714 | +341% | 0 | 0 | — |
case-10 | fail→pass | 19,137 | 15,165 | -21% | 1 | 1 | 0% | 2,653 | 10,726 | +304% | 0 | 0 | — |
case-11 | fail→pass | 18,631 | 12,639 | -32% | 1 | 1 | 0% | 2,608 | 10,148 | +289% | 0 | 0 | — |
case-12 | fail→pass | 24,790 | 18,862 | -24% | 1 | 1 | 0% | 3,518 | 11,274 | +220% | 0 | 0 | — |
case-13 | fail→pass | 18,653 | 15,742 | -16% | 1 | 1 | 0% | 2,514 | 10,875 | +333% | 0 | 0 | — |
case-14 | pass→pass | 21,907 | 8,441 | -61% | 1 | 1 | 0% | 3,241 | 10,326 | +219% | 0 | 0 | — |
case-15 | fail→pass | 28,411 | 13,214 | -53% | 1 | 1 | 0% | 4,301 | 10,440 | +143% | 0 | 0 | — |
case-16 | fail→pass | 27,385 | 9,917 | -64% | 1 | 1 | 0% | 4,096 | 9,550 | +133% | 0 | 0 | — |
case-17 | fail→pass | 19,594 | 17,176 | -12% | 1 | 1 | 0% | 2,666 | 11,122 | +317% | 0 | 0 | — |
case-18 | fail→pass | 29,499 | 23,074 | -22% | 1 | 1 | 0% | 4,770 | 12,420 | +160% | 0 | 0 | — |
case-19 | fail→pass | 20,223 | 15,551 | -23% | 1 | 1 | 0% | 2,781 | 10,783 | +288% | 0 | 0 | — |
case-20 | fail→pass | 31,329 | 10,883 | -65% | 1 | 1 | 0% | 4,806 | 9,868 | +105% | 0 | 0 | — |
case-21 | fail→pass | 21,095 | 15,507 | -26% | 1 | 1 | 0% | 3,180 | 11,012 | +246% | 0 | 0 | — |
case-22 | pass→pass | 20,444 | 13,562 | -34% | 1 | 1 | 0% | 3,318 | 10,433 | +214% | 0 | 0 | — |
case-23 | pass→pass | 18,638 | 16,542 | -11% | 1 | 1 | 0% | 2,931 | 11,163 | +281% | 0 | 0 | — |
case-24 | pass→pass | 15,804 | 14,051 | -11% | 1 | 1 | 0% | 2,045 | 10,446 | +411% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 24 cases were attempted. The headline lift of +79 percentage points is the difference between those two pass rates over the 24 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.