{"version":3,"file":"bankr-leverage.mjs","names":[],"sources":["../../../src/tools/bankr-leverage.ts"],"sourcesContent":["/**\n * Bankr Leverage Tool — leveraged trading via Avantis on Base.\n *\n * Open long/short positions with up to 10x leverage on crypto pairs,\n * forex, and commodities. View and close existing positions.\n * All operations go through Bankr's prompt API.\n *\n * Requires Bankr wallet (/connect_bankr).\n */\n\nimport { Type } from '@sinclair/typebox';\nimport { stringEnum, jsonResult, errorResult, readStringParam, readNumberParam } from '../lib/tool-helpers.js';\nimport { hasBankrApi } from '../services/bankr-api.js';\n\nconst ACTIONS = ['long', 'short', 'close', 'positions'] as const;\n\nconst BankrLeverageSchema = Type.Object({\n  action: stringEnum(ACTIONS, {\n    description:\n      'long: open a long position. short: open a short position. ' +\n      'close: close an existing position. positions: view open positions.',\n  }),\n  pair: Type.Optional(Type.String({\n    description: 'Trading pair (e.g. \"BTC/USD\", \"ETH/USD\", \"GOLD\", \"EUR/USD\").',\n  })),\n  amount: Type.Optional(Type.String({\n    description: 'Dollar amount for the position (e.g. \"100\", \"500\").',\n  })),\n  leverage: Type.Optional(Type.Number({\n    description: 'Leverage multiplier: 1-10 (default: 1). Higher = more risk.',\n  })),\n  stop_loss: Type.Optional(Type.String({\n    description: 'Stop-loss percentage (e.g. \"5%\", \"10%\"). Closes position if loss exceeds this.',\n  })),\n  take_profit: Type.Optional(Type.String({\n    description: 'Take-profit percentage (e.g. \"50%\", \"200%\"). Closes position when profit reaches this.',\n  })),\n});\n\n// ─── Input Sanitization (C3: prevent prompt injection) ──────────────────\nconst SAFE_PAIR_RE = /^[A-Za-z0-9/.\\-_ ]{1,30}$/;\nconst SAFE_AMOUNT_RE = /^\\$?[0-9][0-9,._]*$/;\nconst SAFE_PERCENT_RE = /^[0-9]{1,5}%?$/;\n\nfunction sanitizePair(input: string): string {\n  const trimmed = input.trim();\n  if (SAFE_PAIR_RE.test(trimmed)) return trimmed;\n  throw new Error(`Invalid pair: \"${trimmed.slice(0, 20)}\". Use format like \"BTC/USD\" or \"ETH/USD\".`);\n}\nfunction sanitizeAmount(input: string): string {\n  const trimmed = input.trim();\n  if (SAFE_AMOUNT_RE.test(trimmed)) return trimmed;\n  throw new Error(`Invalid amount: \"${trimmed.slice(0, 20)}\". Use a number like \"100\" or \"500\".`);\n}\nfunction sanitizePercent(input: string): string {\n  const trimmed = input.trim();\n  if (SAFE_PERCENT_RE.test(trimmed)) return trimmed;\n  throw new Error(`Invalid percentage: \"${trimmed.slice(0, 20)}\". Use format like \"5%\" or \"200%\".`);\n}\n\nfunction buildPrompt(action: string, params: Record<string, unknown>): string {\n  const pair = readStringParam(params, 'pair') ? sanitizePair(readStringParam(params, 'pair')!) : '';\n  const amount = readStringParam(params, 'amount') ? sanitizeAmount(readStringParam(params, 'amount')!) : '';\n  const leverage = readNumberParam(params, 'leverage');\n  const stopLoss = readStringParam(params, 'stop_loss') ? sanitizePercent(readStringParam(params, 'stop_loss')!) : undefined;\n  const takeProfit = readStringParam(params, 'take_profit') ? sanitizePercent(readStringParam(params, 'take_profit')!) : undefined;\n\n  const leverageStr = leverage && leverage > 1 ? ` with ${leverage}x leverage` : '';\n  const slStr = stopLoss ? `, ${stopLoss} stop loss` : '';\n  const tpStr = takeProfit ? `, ${takeProfit} take profit` : '';\n\n  switch (action) {\n    case 'long':\n      return `long $${amount} ${pair}${leverageStr}${slStr}${tpStr}`;\n    case 'short':\n      return `short $${amount} ${pair}${leverageStr}${slStr}${tpStr}`;\n    case 'close':\n      return `close my ${pair} position`;\n    case 'positions':\n      return 'show my Avantis positions';\n    default:\n      return action;\n  }\n}\n\nexport function createBankrLeverageTool() {\n  return {\n    name: 'bankr_leverage',\n    label: 'Bankr Leverage',\n    ownerOnly: true,\n    description:\n      'Leveraged trading via Avantis on Base. Open long/short positions ' +\n      'with 1-10x leverage on crypto (BTC, ETH), forex (EUR/USD), and ' +\n      'commodities (GOLD). Set stop-loss and take-profit levels. ' +\n      'WARNING: Leveraged trading carries significant risk of loss. ' +\n      'Requires Bankr wallet (/connect_bankr).',\n    parameters: BankrLeverageSchema,\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      if (!hasBankrApi()) {\n        return errorResult(\n          'Bankr API key not configured. Connect via /connect_bankr first.'\n        );\n      }\n\n      // Validate required params\n      if (action === 'long' || action === 'short') {\n        if (!readStringParam(params, 'pair')) {\n          return errorResult(`\"pair\" is required for ${action} (e.g. \"BTC/USD\", \"ETH/USD\").`);\n        }\n        if (!readStringParam(params, 'amount')) {\n          return errorResult(`\"amount\" is required for ${action} (dollar amount).`);\n        }\n        const leverage = readNumberParam(params, 'leverage');\n        if (leverage !== undefined && (leverage < 1 || leverage > 10)) {\n          return errorResult('Leverage must be between 1 and 10.');\n        }\n      }\n\n      if (action === 'close' && !readStringParam(params, 'pair')) {\n        return errorResult('\"pair\" is required for close (e.g. \"BTC/USD\").');\n      }\n\n      try {\n        const { bankrPromptAndPoll } = await import('../services/bankr-api.js');\n\n        const prompt = buildPrompt(action, params);\n        const timeoutMs = action === 'positions' ? 30_000 : 60_000;\n        const result = await bankrPromptAndPoll(prompt, { timeoutMs });\n\n        if (result.status === 'failed') {\n          return errorResult(`Leverage ${action} failed: ${result.error ?? 'Unknown error'}`);\n        }\n\n        return jsonResult({\n          status: 'success',\n          action,\n          chain: 'base',\n          protocol: 'avantis',\n          response: result.response,\n          richData: result.richData,\n          transactions: result.transactions,\n        });\n      } catch (err) {\n        return errorResult(\n          `Leverage ${action} failed: ${err instanceof Error ? err.message : String(err)}`\n        );\n      }\n    },\n  };\n}\n"],"mappings":"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"}