{"path":"oracle/src/chain.ts","size":4641,"locked":false,"binary":false,"truncated":false,"content":"import * as anchor from \"@coral-xyz/anchor\";\nimport { PublicKey, Keypair, SystemProgram, Connection } from \"@solana/web3.js\";\nimport bs58 from \"bs58\";\nimport { config } from \"./config\";\nimport { GigEscrow, StoredJob } from \"./types\";\nimport { deriveVaultPda } from \"./utils\";\n\n// ─── PROGRAM SETUP ────────────────────────────────────────────────────────────\n\nfunction loadOracleKeypair(): Keypair {\n  let decoded: Uint8Array;\n  try {\n    decoded = bs58.decode(config.solana.oraclePrivateKey);\n  } catch (err: unknown) {\n    const message = err instanceof Error ? err.message : String(err);\n    throw new Error(`Failed to base58-decode ORACLE_PRIVATE_KEY: ${message}`);\n  }\n  try {\n    return Keypair.fromSecretKey(decoded);\n  } catch (err: unknown) {\n    const message = err instanceof Error ? err.message : String(err);\n    throw new Error(\n      `ORACLE_PRIVATE_KEY decoded but is not a valid Solana secret key (${decoded.length} bytes): ${message}`\n    );\n  }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-var-requires\nconst idl = require(\"../idl.json\");\n\nfunction buildProgram() {\n  const oracleKeypair = loadOracleKeypair();\n  const connection    = new Connection(config.solana.rpcUrl, \"confirmed\");\n  const wallet        = new anchor.Wallet(oracleKeypair);\n  const provider      = new anchor.AnchorProvider(connection, wallet, {\n    commitment: \"confirmed\",\n    preflightCommitment: \"confirmed\",\n  });\n\n  // Inject the deployed program ID from config, in case idl.json address is a placeholder\n  const idlWithAddress = { ...idl, address: config.solana.programId };\n  const program = new anchor.Program(idlWithAddress as anchor.Idl, provider);\n\n  return { program, oracleKeypair, connection };\n}\n\n// Build once and reuse across all calls\nconst { program, oracleKeypair, connection } = buildProgram();\nconst programId = new PublicKey(config.solana.programId);\n\n// ─── FETCH ESCROW ─────────────────────────────────────────────────────────────\n\nexport async function fetchEscrow(escrowPubkey: PublicKey): Promise<GigEscrow> {\n  try {\n    // eslint-disable-next-line @typescript-eslint/no-explicit-any\n    const escrow = await (program.account as any).gigEscrow.fetch(escrowPubkey) as GigEscrow;\n    return escrow;\n  } catch (err: unknown) {\n    const message = err instanceof Error ? err.message : String(err);\n    throw new Error(\n      `Failed to fetch escrow account ${escrowPubkey.toBase58()}: ${message}`\n    );\n  }\n}\n\n// ─── TRANSACTION HELPER ──────────────────────────────────────────────────────\n\nasync function sendOracleTx(\n  method: string,\n  job: StoredJob,\n  extraAccounts: Record<string, PublicKey>\n): Promise<string> {\n  const { escrow, escrowPubkey } = job;\n  const vaultPda = deriveVaultPda(escrow.client, escrow.jobId, programId);\n\n  try {\n    // eslint-disable-next-line @typescript-eslint/no-explicit-any\n    const txSig = await (program.methods as any)\n      [method]()\n      .accounts({\n        ...extraAccounts,\n        client:        escrow.client,\n        escrowAccount: escrowPubkey,\n        vaultAccount:  vaultPda,\n        systemProgram: SystemProgram.programId,\n      })\n      .signers([oracleKeypair])\n      .rpc({ commitment: \"confirmed\" });\n\n    return txSig as string;\n  } catch (err: unknown) {\n    const message = err instanceof Error ? err.message : String(err);\n    throw new Error(\n      `${method} tx failed for job \"${escrow.jobId}\" (escrow: ${escrowPubkey.toBase58()}): ${message}`\n    );\n  }\n}\n\n// ─── RELEASE PAYMENT ───────────────────────────────────────────────────────────\n\nexport async function releasePayment(job: StoredJob): Promise<string> {\n  return sendOracleTx(\"releasePayment\", job, {\n    authority:  oracleKeypair.publicKey,\n    freelancer: job.escrow.freelancer,\n  });\n}\n\n// ─── CANCEL JOB ───────────────────────────────────────────────────────────────\n\nexport async function cancelJob(job: StoredJob): Promise<string> {\n  return sendOracleTx(\"cancelJob\", job, {\n    oracle: oracleKeypair.publicKey,\n  });\n}\n\nexport { connection, oracleKeypair };\n"}