/**
* Mainnet Script: Create V2 AMM Market with Custom Oracle
*
* Creates a prediction market on Solana mainnet with YOUR own oracle.
* Only your oracle wallet can resolve/settle this market.
*
* Usage:
* tsx scripts/createMarketCustomOracle.ts
*
* Environment Variables:
* PNP_PRIVATE_KEY - Your wallet private key (base58 or JSON array)
* ORACLE_ADDRESS - (Optional) Custom oracle address. Defaults to your wallet.
*/
import { createRequire } from 'module';
import { PublicKey } from '@solana/web3.js';
import { getAssociatedTokenAddressSync } from '@solana/spl-token';
import { config } from 'dotenv';
config();
const require = createRequire(import.meta.url);
const { PNPClient } = require('pnp-sdk');
// =====================================================
// ========== MAINNET CONFIGURATION ===================
// =====================================================
const RPC_URL = process.env.RPC_URL || 'https://api.mainnet-beta.solana.com';
const PRIVATE_KEY = process.env.PNP_PRIVATE_KEY;
if (!PRIVATE_KEY) {
console.error('Private key not found in environment');
console.log('\nSet it in your .env file:');
console.log(' PNP_PRIVATE_KEY=your_base58_private_key_here');
process.exit(1);
}
// Collateral token (USDC on mainnet)
const COLLATERAL_MINT = new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v');
// ========== MARKET PARAMETERS =======================
const QUESTION = process.env.MARKET_QUESTION ||
'Will this event happen? (Custom Oracle Market)';
const INITIAL_LIQUIDITY = BigInt(
process.env.INITIAL_LIQUIDITY || '10000000' // 10 USDC (6 decimals)
);
const DAYS_UNTIL_END = Number(process.env.DAYS_UNTIL_END || '30');
const END_TIME = BigInt(Math.floor(Date.now() / 1000) + DAYS_UNTIL_END * 24 * 60 * 60);
// Optional: Custom YES odds in basis points (100-9900). Default is 5000 (50/50)
const YES_ODDS_BPS = process.env.YES_ODDS_BPS ? Number(process.env.YES_ODDS_BPS) : undefined;
// =====================================================
async function main() {
console.log('\nPNP SDK - Mainnet Market Creation with Custom Oracle\n');
const secretKey = PNPClient.parseSecretKey(PRIVATE_KEY);
const client = new PNPClient(RPC_URL, secretKey);
console.log('Connected to Solana');
console.log(` Program ID: ${client.client.programId.toBase58()}`);
console.log(` Network: ${client.client.isDevnet ? 'DEVNET' : 'MAINNET'}`);
if (!client.anchorMarket) {
throw new Error('AnchorMarket module not available. Check your private key.');
}
const walletPubkey = client.signer!.publicKey;
// Custom oracle: use env var or default to your own wallet
const ORACLE_ADDRESS = process.env.ORACLE_ADDRESS
? new PublicKey(process.env.ORACLE_ADDRESS)
: walletPubkey; // Default: you are the oracle
console.log('\nMarket Configuration:');
console.log(` Wallet: ${walletPubkey.toBase58()}`);
console.log(` Question: ${QUESTION}`);
console.log(` Collateral Mint: ${COLLATERAL_MINT.toBase58()}`);
console.log(` Initial Liquidity: ${INITIAL_LIQUIDITY.toString()} (raw units)`);
console.log(` End Time: ${new Date(Number(END_TIME) * 1000).toISOString()}`);
console.log(` Custom Oracle: ${ORACLE_ADDRESS.toBase58()}`);
if (YES_ODDS_BPS) {
console.log(` YES Odds: ${YES_ODDS_BPS / 100}%`);
}
// Check collateral balance
const tokenAta = getAssociatedTokenAddressSync(COLLATERAL_MINT, walletPubkey);
console.log('\nChecking collateral balance...');
try {
const balance = await client.client.connection.getTokenAccountBalance(tokenAta);
const balanceAmount = BigInt(balance.value.amount);
console.log(` Balance: ${balance.value.uiAmountString} (${balanceAmount} raw)`);
if (balanceAmount < INITIAL_LIQUIDITY) {
console.error(`\nInsufficient balance!`);
console.log(` Have: ${balance.value.uiAmountString}`);
console.log(` Need: ${Number(INITIAL_LIQUIDITY) / 1_000_000}`);
process.exit(1);
}
console.log(' Sufficient balance');
} catch (error: unknown) {
const msg = error instanceof Error ? error.message : String(error);
console.error(`\nToken account not found: ${msg}`);
process.exit(1);
}
// Create market with custom oracle
console.log('\nCreating market with custom oracle...');
const createRes = await client.createMarketWithCustomOracle({
question: QUESTION,
initialLiquidity: INITIAL_LIQUIDITY,
endTime: END_TIME,
collateralMint: COLLATERAL_MINT,
settlerAddress: ORACLE_ADDRESS,
yesOddsBps: YES_ODDS_BPS,
});
console.log('Confirming transaction...');
await client.client.connection.confirmTransaction(createRes.signature, 'confirmed');
// Output result
const result = {
success: true,
network: client.client.isDevnet ? 'devnet' : 'mainnet',
market: createRes.market.toBase58(),
signature: createRes.signature,
question: QUESTION,
customOracle: ORACLE_ADDRESS.toBase58(),
collateralMint: COLLATERAL_MINT.toBase58(),
initialLiquidity: INITIAL_LIQUIDITY.toString(),
endTime: new Date(Number(END_TIME) * 1000).toISOString(),
explorerUrl: `https://explorer.solana.com/address/${createRes.market.toBase58()}`,
txUrl: `https://explorer.solana.com/tx/${createRes.signature}`
};
console.log('\nMARKET CREATED SUCCESSFULLY WITH CUSTOM ORACLE!');
console.log(JSON.stringify(result, null, 2));
console.log('\nImportant Next Steps:');
console.log(` 1. Call setMarketResolvable(true) within 15 minutes to enable trading`);
console.log(` 2. Only ${ORACLE_ADDRESS.toBase58()} can resolve this market`);
console.log(` 3. PNP's AI oracle has NO authority over this market`);
console.log(` 4. After end time, your oracle must settle the market`);
}
main().catch((err) => {
console.error('\nError:', err.message || err);
if (err.logs) {
console.error('Program logs:', err.logs);
}
process.exit(1);
});