{"version":3,"file":"airdrop.mjs","names":[],"sources":["../../../src/tools/airdrop.ts"],"sourcesContent":["/**\n * Airdrop Tool — check eligibility and claim airdrops.\n *\n * Actions:\n *   list       — List known active/recent airdrops\n *   check      — Check eligibility for a specific airdrop\n *   check_all  — Check eligibility across all active airdrops\n *   claim      — Generate claim calldata for eligible airdrop\n *\n * Uses AirdropService for eligibility checks and claim calldata generation.\n * Falls back to PinchTab browser tool for dApps that require UI interaction.\n */\n\nimport { Type } from '@sinclair/typebox';\nimport { stringEnum, jsonResult, errorResult, readStringParam, readNumberParam } from '../lib/tool-helpers.js';\nimport { getAirdropService } from '../services/airdrop-service.js';\nimport {\n  getWalletState,\n  requirePublicClient,\n} from '../services/walletconnect-service.js';\nimport { resolveAddressOrEns, isEnsName } from '../lib/ens-resolver.js';\n\nconst ACTIONS = ['list', 'check', 'check_all', 'claim'] as const;\n\nconst AirdropSchema = Type.Object({\n  action: stringEnum(ACTIONS, {\n    description:\n      'list: list known airdrops. check: check eligibility for one airdrop. ' +\n      'check_all: check all active airdrops for your address. ' +\n      'claim: generate claim transaction for an eligible airdrop.',\n  }),\n  airdrop_id: Type.Optional(Type.String({\n    description: 'Airdrop ID (e.g. \"eigen-s2\", \"morpho\"). Required for check and claim.',\n  })),\n  address: Type.Optional(Type.String({\n    description: 'Address or ENS to check. Defaults to connected wallet.',\n  })),\n  status: Type.Optional(Type.String({\n    description: 'Filter by status: \"active\" (default), \"ended\", \"upcoming\", \"all\".',\n  })),\n  chain: Type.Optional(Type.String({\n    description: 'Filter by chain: \"ethereum\", \"base\", \"arbitrum\", \"optimism\", \"polygon\".',\n  })),\n  claim_index: Type.Optional(Type.Number({\n    description: 'Merkle claim index. Required for claim action (from check result).',\n  })),\n  amount: Type.Optional(Type.String({\n    description: 'Claim amount in wei. Required for claim action (from check result).',\n  })),\n  proof: Type.Optional(Type.String({\n    description: 'JSON-encoded merkle proof array. Required for claim action (from check result).',\n  })),\n});\n\nexport function createAirdropTool() {\n  return {\n    name: 'airdrop',\n    label: 'Airdrop Tracker',\n    ownerOnly: true,\n    description:\n      'Check airdrop eligibility and claim tokens. Tracks known active airdrops ' +\n      '(EigenLayer, LayerZero, Scroll, Morpho, etc.). For airdrops requiring browser ' +\n      'interaction, guides you to use the browser tool.',\n    parameters: AirdropSchema,\n    execute: async (_toolCallId: string, args: unknown) => {\n      const params = args as Record<string, unknown>;\n      const action = readStringParam(params, 'action', { required: true })!;\n\n      switch (action) {\n        case 'list':\n          return handleList(params);\n        case 'check':\n          return handleCheck(params);\n        case 'check_all':\n          return handleCheckAll(params);\n        case 'claim':\n          return handleClaim(params);\n        default:\n          return errorResult(`Unknown action: ${action}. Use: list, check, check_all, claim`);\n      }\n    },\n  };\n}\n\n// ── Action Handlers ─────────────────────────────────────────────────────\n\nfunction handleList(params: Record<string, unknown>) {\n  const status = (readStringParam(params, 'status') ?? 'active') as any;\n  const chain = readStringParam(params, 'chain') ?? undefined;\n\n  const service = getAirdropService();\n  const airdrops = service.listAirdrops({ status, chain });\n\n  if (airdrops.length === 0) {\n    return jsonResult({\n      airdrops: [],\n      message: `No ${status} airdrops found${chain ? ` on ${chain}` : ''}.`,\n    });\n  }\n\n  return jsonResult({\n    count: airdrops.length,\n    filter: { status, chain: chain ?? 'all' },\n    airdrops: airdrops.map(a => ({\n      id: a.id,\n      name: a.name,\n      token: a.tokenSymbol,\n      chain: a.chain,\n      status: a.status,\n      requiresBrowser: a.requiresBrowser ?? false,\n      deadline: a.deadline,\n      description: a.description,\n    })),\n    tip: 'Use action=check with an airdrop_id to check eligibility, or action=check_all to scan all active airdrops.',\n  });\n}\n\nasync function handleCheck(params: Record<string, unknown>) {\n  const airdropId = readStringParam(params, 'airdrop_id');\n  if (!airdropId) {\n    return errorResult('airdrop_id is required. Use action=list to see available airdrops.');\n  }\n\n  const address = await resolveAddress(params);\n  if (!address) {\n    return errorResult('No wallet connected and no address provided.');\n  }\n\n  const service = getAirdropService();\n  const airdrop = service.getAirdrop(airdropId);\n\n  let publicClient: any;\n  try { publicClient = requirePublicClient(); } catch { /* optional */ }\n\n  const result = await service.checkEligibility(airdropId, address, publicClient);\n\n  return jsonResult({\n    ...result,\n    chain: airdrop?.chain,\n    address,\n    tip: result.eligible\n      ? 'Use action=claim with the proof data to generate the claim transaction.'\n      : result.error?.includes('browser')\n        ? `Use the browser tool to check ${airdrop?.name ?? airdropId} eligibility at the project\\'s claim page.`\n        : undefined,\n  });\n}\n\nasync function handleCheckAll(params: Record<string, unknown>) {\n  const address = await resolveAddress(params);\n  if (!address) {\n    return errorResult('No wallet connected and no address provided.');\n  }\n\n  const service = getAirdropService();\n  let publicClient: any;\n  try { publicClient = requirePublicClient(); } catch { /* optional */ }\n\n  const results = await service.checkAll(address, publicClient);\n\n  const eligible = results.filter(r => r.eligible);\n  const needsBrowser = results.filter(r => r.error?.includes('browser'));\n  const notEligible = results.filter(r => !r.eligible && !r.error?.includes('browser'));\n\n  return jsonResult({\n    address,\n    totalChecked: results.length,\n    eligible: eligible.map(r => ({\n      id: r.airdropId,\n      name: r.airdropName,\n      amount: r.amountFormatted ?? r.amount,\n      claimed: r.claimed,\n    })),\n    needsBrowserCheck: needsBrowser.map(r => ({\n      id: r.airdropId,\n      name: r.airdropName,\n    })),\n    notEligible: notEligible.map(r => r.airdropName),\n    tip: needsBrowser.length > 0\n      ? `${needsBrowser.length} airdrop(s) require browser-based eligibility checks. Use the browser tool to visit their claim pages.`\n      : undefined,\n  });\n}\n\nasync function handleClaim(params: Record<string, unknown>) {\n  const airdropId = readStringParam(params, 'airdrop_id');\n  if (!airdropId) {\n    return errorResult('airdrop_id is required for claim.');\n  }\n\n  const address = await resolveAddress(params);\n  if (!address) {\n    return errorResult('No wallet connected and no address provided.');\n  }\n\n  const claimIndex = readNumberParam(params, 'claim_index');\n  const amount = readStringParam(params, 'amount');\n  const proofStr = readStringParam(params, 'proof');\n\n  if (claimIndex === undefined || claimIndex === null || !amount || !proofStr) {\n    return errorResult(\n      'claim_index, amount, and proof are required for claim. ' +\n      'Run action=check first to get these values from the eligibility check.',\n    );\n  }\n\n  let proof: string[];\n  try {\n    proof = JSON.parse(proofStr);\n    if (!Array.isArray(proof)) throw new Error('not array');\n  } catch {\n    return errorResult('proof must be a JSON array of hex strings (bytes32[]).');\n  }\n\n  const service = getAirdropService();\n  const airdrop = service.getAirdrop(airdropId);\n\n  const calldata = service.generateClaimCalldata(\n    airdropId,\n    claimIndex,\n    address,\n    amount,\n    proof,\n  );\n\n  if (!calldata) {\n    return errorResult(\n      `Cannot generate claim calldata for ${airdropId}. ` +\n      (airdrop?.requiresBrowser\n        ? 'This airdrop requires browser-based claiming. Use the browser tool.'\n        : 'Unknown airdrop or invalid claim contract.'),\n    );\n  }\n\n  return jsonResult({\n    status: 'calldata_ready',\n    airdropId,\n    airdropName: airdrop?.name ?? airdropId,\n    chain: airdrop?.chain ?? 'ethereum',\n    transaction: {\n      to: calldata.to,\n      data: calldata.data,\n      value: calldata.value,\n    },\n    description: calldata.description,\n    note: 'Submit this transaction via your wallet to claim the airdrop. The transaction will call the claim function on the distributor contract.',\n  });\n}\n\n// ── Helpers ──────────────────────────────────────────────────────────────\n\nasync function resolveAddress(params: Record<string, unknown>): Promise<string | null> {\n  let address = readStringParam(params, 'address');\n\n  if (address && isEnsName(address)) {\n    try {\n      const publicClient = requirePublicClient();\n      const resolved = await resolveAddressOrEns(address, publicClient);\n      return resolved.address;\n    } catch {\n      // fall through\n    }\n  }\n\n  if (address) return address;\n\n  // Fall back to connected wallet\n  const state = getWalletState();\n  return state.address ?? null;\n}\n"],"mappings":"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"}