{"path":"wiki/SDK-Reference.md","size":8766,"locked":false,"binary":false,"truncated":false,"content":"# SDK Reference\n\nThe `@mappers-protocol/sdk` package (`lib/sdk/`) provides TypeScript clients for interacting with the Mappers Protocol — both the on-chain escrow program and the oracle HTTP API.\n\n---\n\n## Installation\n\nThe SDK is a workspace package. Within the monorepo, add it as a dependency:\n\n```json\n{\n  \"dependencies\": {\n    \"@mappers-protocol/sdk\": \"workspace:*\"\n  }\n}\n```\n\n---\n\n## Exports\n\n```typescript\nimport {\n  // On-chain client\n  MappersClient,\n\n  // Oracle HTTP client\n  OracleClient,\n  OracleError,\n\n  // PDA derivation\n  deriveEscrowPda,\n  deriveVaultPda,\n\n  // Constants\n  MAPPERS_PROGRAM_ID,\n  PDA_SEEDS,\n  JOB_ID_MAX_LENGTH,\n  MINIMUM_ESCROW_LAMPORTS,\n\n  // Types\n  type GigEscrowAccount,\n  type FetchedEscrow,\n  type InitializeJobParams,\n  type ReleasePaymentParams,\n  type CancelJobParams,\n  type JobStatus,\n  type SubmitRequest,\n  type SubmitResponse,\n  type OracleHealthResponse,\n  type OracleJobResponse,\n  type DeliverableType,\n  type ConsensusOutcome,\n\n  // Type guards\n  isJobPending,\n  isJobCompleted,\n  isJobCancelled,\n} from \"@mappers-protocol/sdk\";\n```\n\n---\n\n## MappersClient\n\nThe on-chain client wraps the Anchor program and provides typed methods for all escrow instructions.\n\n### Constructor\n\n```typescript\nimport { AnchorProvider } from \"@coral-xyz/anchor\";\nimport { MappersClient, MAPPERS_PROGRAM_ID } from \"@mappers-protocol/sdk\";\n\nconst provider = AnchorProvider.env(); // or construct your own\nconst client = new MappersClient(provider);\n\n// Optionally pass a custom program ID (e.g., for localnet)\nconst localClient = new MappersClient(provider, myLocalProgramId);\n```\n\n### Initialize a Job\n\nCreate an escrow and deposit SOL into the vault:\n\n```typescript\nimport { PublicKey } from \"@solana/web3.js\";\nimport { BN } from \"@coral-xyz/anchor\";\n\nconst txSig = await client.initializeJob({\n  jobId: \"project-alpha-001\",          // max 32 characters\n  amount: new BN(5_000_000_000),       // 5 SOL in lamports\n  freelancer: new PublicKey(\"...\"),     // freelancer wallet\n  oracle: new PublicKey(\"...\"),        // oracle authority\n});\n\nconsole.log(\"Job initialized:\", txSig);\n```\n\n**Validations:**\n- `jobId` must be <= 32 characters (throws locally if exceeded)\n- `amount` must be >= 890,880 lamports (enforced on-chain)\n- The signer (from the provider wallet) becomes the `client`\n\n### Release Payment\n\nPay the freelancer and close the escrow:\n\n```typescript\nconst escrow = await client.fetchEscrowByJobId(clientPubkey, \"project-alpha-001\");\nconst [escrowPda] = client.deriveEscrowPda(clientPubkey, \"project-alpha-001\");\n\nconst txSig = await client.releasePayment({\n  escrowPubkey: escrowPda,\n  escrow,\n});\n```\n\nThe signer must be either the `client` or the `oracle` stored on the escrow account.\n\n### Cancel a Job\n\nRefund the client (oracle-only):\n\n```typescript\nconst txSig = await client.cancelJob({\n  escrowPubkey: escrowPda,\n  escrow,\n});\n```\n\nThe signer must be the `oracle` stored on the escrow account.\n\n### Fetch Escrow Accounts\n\n```typescript\n// Fetch a single escrow by its public key\nconst escrow = await client.fetchEscrow(escrowPubkey);\n\n// Fetch by client + jobId (derives the PDA internally)\nconst escrow = await client.fetchEscrowByJobId(clientPubkey, \"project-alpha-001\");\n\n// Fetch all escrows in the program\nconst all = await client.fetchAllEscrows();\n// Returns: FetchedEscrow[] — each has { publicKey, account }\n\n// Fetch all escrows for a specific client\nconst clientEscrows = await client.fetchEscrowsByClient(clientPubkey);\n```\n\n### PDA Derivation\n\nDerive PDA addresses without making RPC calls:\n\n```typescript\nconst [escrowPda, escrowBump] = client.deriveEscrowPda(clientPubkey, jobId);\nconst [vaultPda, vaultBump] = client.deriveVaultPda(clientPubkey, jobId);\n\n// Or use standalone functions\nimport { deriveEscrowPda, deriveVaultPda } from \"@mappers-protocol/sdk\";\nconst [pda, bump] = deriveEscrowPda(clientPubkey, jobId);\n```\n\n---\n\n## OracleClient\n\nHTTP client for the oracle middleware API.\n\n### Constructor\n\n```typescript\nimport { OracleClient } from \"@mappers-protocol/sdk\";\n\nconst oracle = new OracleClient(\"http://localhost:3001\");\n```\n\n### Health Check\n\n```typescript\nconst health = await oracle.health();\n// Returns: { status: string, pendingJobs: number, timestamp: string }\n```\n\n### Get Job State\n\n```typescript\nconst job = await oracle.getJob(\"project-alpha-001\");\n// Returns: {\n//   jobId: string,\n//   escrowPubkey: string,\n//   client: string,\n//   freelancer: string,\n//   amount: string,\n//   status: \"pending\" | \"completed\" | \"cancelled\",\n//   detectedAt: number\n// }\n```\n\n### Submit a Deliverable\n\nTrigger AI verification for a job:\n\n```typescript\nconst result = await oracle.submitDeliverable({\n  jobId: \"project-alpha-001\",\n  description: \"Build a landing page with hero section and contact form\",\n  acceptanceCriteria: [\n    \"Responsive design (mobile + desktop)\",\n    \"Contact form sends email\",\n    \"Hero section has CTA button\",\n  ],\n  deliverable: \"https://example.com/landing-page\",\n  deliverableType: \"url\",\n});\n\n// Returns: {\n//   success: boolean,\n//   jobId: string,\n//   outcome?: \"RELEASE\" | \"REFUND\" | \"ESCALATE\",\n//   txSig?: string,\n//   error?: string\n// }\n\nif (result.outcome === \"RELEASE\") {\n  console.log(\"Payment released! Tx:\", result.txSig);\n} else if (result.outcome === \"REFUND\") {\n  console.log(\"Job cancelled, client refunded. Tx:\", result.txSig);\n} else if (result.outcome === \"ESCALATE\") {\n  console.log(\"Escalated to human arbitration.\");\n}\n```\n\n### Deliverable Types\n\n| Type | Description | Example |\n|---|---|---|\n| `\"url\"` | Public URL to the deliverable | `\"https://example.com/project\"` |\n| `\"ipfs\"` | IPFS content identifier | `\"QmXyz...\"` |\n| `\"text\"` | Raw text content | `\"Here is the completed article...\"` |\n| `\"json\"` | Structured JSON payload | `'{\"report\": {...}}'` |\n\n### Error Handling\n\nAll errors from the oracle client are thrown as `OracleError`:\n\n```typescript\nimport { OracleClient, OracleError } from \"@mappers-protocol/sdk\";\n\ntry {\n  const result = await oracle.submitDeliverable(payload);\n} catch (err) {\n  if (err instanceof OracleError) {\n    console.error(`Oracle error (${err.statusCode}): ${err.message}`);\n    // Handles:\n    // - HTTP errors (4xx, 5xx) with JSON error bodies\n    // - Non-JSON responses (e.g., nginx 502 proxy errors)\n    // - Connection failures\n  }\n}\n```\n\n---\n\n## Types\n\n### GigEscrowAccount\n\n```typescript\ninterface GigEscrowAccount {\n  client: PublicKey;\n  freelancer: PublicKey;\n  oracle: PublicKey;\n  amount: BN;           // lamports locked in the vault\n  jobId: string;\n  status: JobStatus;\n  escrowBump: number;\n  vaultBump: number;\n}\n```\n\n### JobStatus\n\nAnchor represents enums as objects with a single key:\n\n```typescript\ntype JobStatus =\n  | { pending: Record<string, never> }\n  | { completed: Record<string, never> }\n  | { cancelled: Record<string, never> };\n\n// Use the type guards:\nimport { isJobPending, isJobCompleted, isJobCancelled } from \"@mappers-protocol/sdk\";\n\nif (isJobPending(escrow.status)) {\n  // Job is still active\n}\n```\n\n### ConsensusOutcome\n\n```typescript\ntype ConsensusOutcome = \"RELEASE\" | \"REFUND\" | \"ESCALATE\";\n```\n\n---\n\n## Constants\n\n```typescript\nimport {\n  MAPPERS_PROGRAM_ID,       // PublicKey — devnet program address\n  PDA_SEEDS,                // { ESCROW: \"gig-escrow\", VAULT: \"vault\" }\n  JOB_ID_MAX_LENGTH,        // 32\n  MINIMUM_ESCROW_LAMPORTS,  // 890_880\n} from \"@mappers-protocol/sdk\";\n```\n\n---\n\n## Full Example\n\n```typescript\nimport { Connection, Keypair, PublicKey } from \"@solana/web3.js\";\nimport { AnchorProvider, Wallet, BN } from \"@coral-xyz/anchor\";\nimport { MappersClient, OracleClient } from \"@mappers-protocol/sdk\";\n\n// Set up provider\nconst connection = new Connection(\"https://api.devnet.solana.com\");\nconst wallet = new Wallet(Keypair.generate());\nconst provider = new AnchorProvider(connection, wallet, {});\n\n// Create clients\nconst mappers = new MappersClient(provider);\nconst oracle = new OracleClient(\"http://localhost:3001\");\n\n// Create a job\nconst jobId = `job-${Date.now()}`;\nconst txSig = await mappers.initializeJob({\n  jobId,\n  amount: new BN(1_000_000_000), // 1 SOL\n  freelancer: new PublicKey(\"FreelancerPubkeyHere\"),\n  oracle: new PublicKey(\"OraclePubkeyHere\"),\n});\n\n// Later — submit a deliverable for AI verification\nconst result = await oracle.submitDeliverable({\n  jobId,\n  description: \"Write a 500-word blog post about Solana DeFi\",\n  acceptanceCriteria: [\"500+ words\", \"Mentions at least 3 DeFi protocols\", \"No factual errors\"],\n  deliverable: \"https://docs.google.com/doc/d/...\",\n  deliverableType: \"url\",\n});\n\nconsole.log(\"Outcome:\", result.outcome);\n```\n\n---\n\nSee the [API Reference](API-Reference.md) for the REST API endpoints, or [Architecture](Architecture.md) for how the SDK fits into the broader system.\n"}