@madeinusmate/grvt-cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1154 -0
- package/dist/chunk-I7C5KKUG.js +46 -0
- package/dist/chunk-I7C5KKUG.js.map +1 -0
- package/dist/cli.js +1880 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +771 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +462 -0
- package/dist/index.d.ts +462 -0
- package/dist/index.js +700 -0
- package/dist/index.js.map +1 -0
- package/dist/schema-LBUHKB7A.js +14 -0
- package/dist/schema-LBUHKB7A.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/config/store.ts","../src/core/config/schema.ts","../src/core/client/endpoints.ts","../src/core/client/http.ts","../src/core/auth/login.ts","../src/core/auth/session.ts","../src/commands/trade/orders.ts","../src/core/output/table.ts","../src/core/output/colors.ts","../src/core/output/format.ts","../src/core/pagination/cursor.ts","../src/core/safety/confirm.ts","../src/core/safety/validate.ts","../src/core/instruments/cache.ts","../src/core/signing/eip712.ts","../src/core/signing/signer.ts","../src/commands/trade/fills.ts","../src/commands/trade/positions.ts","../src/commands/trade/funding-payments.ts","../src/commands/account.ts","../src/commands/funds/deposits.ts","../src/core/currencies/cache.ts","../src/commands/funds/transfers.ts","../src/commands/funds/withdrawals.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync, chmodSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { parse as parseTOML, stringify as stringifyTOML } from \"smol-toml\";\nimport { configSchema, DEFAULT_CONFIG, type GrvtConfig } from \"./schema.js\";\n\nconst getConfigDir = (): string => {\n const xdgConfig = process.env[\"XDG_CONFIG_HOME\"];\n const base = xdgConfig || join(homedir(), \".config\");\n return join(base, \"grvt\");\n};\n\nconst getConfigPath = (): string => join(getConfigDir(), \"config.toml\");\n\nexport const configPath = (): string => getConfigPath();\n\nexport const loadConfig = (): GrvtConfig => {\n const path = getConfigPath();\n if (!existsSync(path)) {\n return DEFAULT_CONFIG;\n }\n\n const raw = readFileSync(path, \"utf-8\");\n const parsed = parseTOML(raw);\n return configSchema.parse(parsed);\n};\n\nexport const saveConfig = (config: GrvtConfig): void => {\n const dir = getConfigDir();\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n const path = getConfigPath();\n const toml = stringifyTOML(config as Record<string, unknown>);\n writeFileSync(path, toml, { mode: 0o600 });\n\n try {\n chmodSync(path, 0o600);\n } catch {\n // best-effort on platforms that don't support chmod\n }\n};\n\nexport const getConfigValue = (config: GrvtConfig, key: string): unknown => {\n const parts = key.split(\".\");\n let current: unknown = config;\n for (const part of parts) {\n if (current === null || current === undefined || typeof current !== \"object\") {\n return undefined;\n }\n current = (current as Record<string, unknown>)[part];\n }\n return current;\n};\n\nexport const setConfigValue = (config: GrvtConfig, key: string, value: string): GrvtConfig => {\n const parts = key.split(\".\");\n const clone = structuredClone(config) as Record<string, unknown>;\n let current = clone;\n\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i]!;\n if (typeof current[part] !== \"object\" || current[part] === null) {\n current[part] = {};\n }\n current = current[part] as Record<string, unknown>;\n }\n\n const lastKey = parts[parts.length - 1]!;\n const coerced = coerceValue(value, key);\n current[lastKey] = coerced;\n\n return configSchema.parse(clone);\n};\n\nexport const unsetConfigValue = (config: GrvtConfig, key: string): GrvtConfig => {\n const parts = key.split(\".\");\n const clone = structuredClone(config) as Record<string, unknown>;\n let current = clone;\n\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i]!;\n if (typeof current[part] !== \"object\" || current[part] === null) {\n return config;\n }\n current = current[part] as Record<string, unknown>;\n }\n\n delete current[parts[parts.length - 1]!];\n return configSchema.parse(clone);\n};\n\nconst NUMERIC_KEYS = new Set([\n \"http.timeoutMs\",\n \"http.retries\",\n \"http.backoffMs\",\n \"http.maxBackoffMs\",\n]);\n\nconst BOOLEAN_KEYS = new Set([\n \"outputDefaults.pretty\",\n \"outputDefaults.silent\",\n]);\n\nconst coerceValue = (value: string, key: string): unknown => {\n if (BOOLEAN_KEYS.has(key) || value === \"true\" || value === \"false\") {\n return value === \"true\";\n }\n if (NUMERIC_KEYS.has(key)) {\n const num = Number(value);\n if (!Number.isNaN(num)) return num;\n }\n return value;\n};\n","import { z } from \"zod\";\n\nconst environmentSchema = z.enum([\"dev\", \"staging\", \"testnet\", \"prod\"]);\n\nconst httpConfigSchema = z.object({\n timeoutMs: z.number().int().positive().default(10000),\n retries: z.number().int().min(0).default(3),\n backoffMs: z.number().int().positive().default(200),\n maxBackoffMs: z.number().int().positive().default(10000),\n});\n\nconst outputDefaultsSchema = z.object({\n output: z.enum([\"json\", \"ndjson\", \"table\", \"raw\"]).default(\"table\"),\n pretty: z.boolean().default(false),\n silent: z.boolean().default(false),\n});\n\nexport const configSchema = z.object({\n env: environmentSchema.default(\"prod\"),\n apiKey: z.string().optional(),\n privateKey: z.string().optional(),\n subAccountId: z.string().optional(),\n accountId: z.string().optional(),\n cookie: z.string().optional(),\n outputDefaults: outputDefaultsSchema.default({}),\n http: httpConfigSchema.default({}),\n});\n\nexport type GrvtConfig = z.infer<typeof configSchema>;\n\nexport const SECRET_KEYS = [\"apiKey\", \"privateKey\", \"cookie\"] as const;\n\nexport const DEFAULT_CONFIG: GrvtConfig = configSchema.parse({});\n\nexport const redactConfig = (config: GrvtConfig): Record<string, unknown> => {\n const redacted = { ...config } as Record<string, unknown>;\n for (const key of SECRET_KEYS) {\n if (redacted[key]) {\n const val = redacted[key] as string;\n redacted[key] = val.length > 8 ? val.slice(0, 4) + \"****\" + val.slice(-4) : \"****\";\n }\n }\n return redacted;\n};\n","export type GrvtEnvironment = \"dev\" | \"staging\" | \"testnet\" | \"prod\";\n\nconst BASE_URLS: Record<GrvtEnvironment, { edge: string; marketData: string; trading: string }> = {\n dev: {\n edge: \"https://edge.dev.gravitymarkets.io\",\n marketData: \"https://market-data.dev.gravitymarkets.io\",\n trading: \"https://trades.dev.gravitymarkets.io\",\n },\n staging: {\n edge: \"https://edge.staging.gravitymarkets.io\",\n marketData: \"https://market-data.staging.gravitymarkets.io\",\n trading: \"https://trades.staging.gravitymarkets.io\",\n },\n testnet: {\n edge: \"https://edge.testnet.grvt.io\",\n marketData: \"https://market-data.testnet.grvt.io\",\n trading: \"https://trades.testnet.grvt.io\",\n },\n prod: {\n edge: \"https://edge.grvt.io\",\n marketData: \"https://market-data.grvt.io\",\n trading: \"https://trades.grvt.io\",\n },\n};\n\nexport const CHAIN_IDS: Record<GrvtEnvironment, { l1: number; l2: number }> = {\n dev: { l1: 11155111, l2: 327 },\n staging: { l1: 11155111, l2: 327 },\n testnet: { l1: 11155111, l2: 326 },\n prod: { l1: 1, l2: 325 },\n};\n\nexport const ENDPOINTS = {\n auth: {\n login: \"/auth/api_key/login\",\n },\n\n marketData: {\n instrument: \"/full/v1/instrument\",\n allInstruments: \"/full/v1/all_instruments\",\n instruments: \"/full/v1/instruments\",\n currency: \"/full/v1/currency\",\n marginRules: \"/full/v1/margin_rules\",\n mini: \"/full/v1/mini\",\n ticker: \"/full/v1/ticker\",\n tradeHistory: \"/full/v1/trade_history\",\n kline: \"/full/v1/kline\",\n funding: \"/full/v1/funding\",\n book: \"/full/v1/book\",\n },\n\n trading: {\n createOrder: \"/full/v1/create_order\",\n cancelOrder: \"/full/v1/cancel_order\",\n cancelAllOrders: \"/full/v1/cancel_all_orders\",\n getOrder: \"/full/v1/order\",\n openOrders: \"/full/v1/open_orders\",\n orderHistory: \"/full/v1/order_history\",\n cancelOnDisconnect: \"/full/v1/cancel_on_disconnect\",\n fillHistory: \"/full/v1/fill_history\",\n positions: \"/full/v1/positions\",\n fundingPaymentHistory: \"/full/v1/funding_payment_history\",\n getAllInitialLeverage: \"/full/v1/get_all_initial_leverage\",\n setInitialLeverage: \"/full/v1/set_initial_leverage\",\n setDeriskMmRatio: \"/full/v1/set_derisk_mm_ratio\",\n },\n\n account: {\n accountSummary: \"/full/v1/account_summary\",\n accountHistory: \"/full/v1/account_history\",\n fundingAccountSummary: \"/full/v1/funding_account_summary\",\n aggregatedAccountSummary: \"/full/v1/aggregated_account_summary\",\n },\n\n funds: {\n depositHistory: \"/full/v1/deposit_history\",\n transfer: \"/full/v1/transfer\",\n transferHistory: \"/full/v1/transfer_history\",\n withdrawal: \"/full/v1/withdrawal\",\n withdrawalHistory: \"/full/v1/withdrawal_history\",\n },\n} as const;\n\nexport const getBaseUrls = (env: GrvtEnvironment) => BASE_URLS[env];\nexport const getChainIds = (env: GrvtEnvironment) => CHAIN_IDS[env];\n","import { type GrvtEnvironment, getBaseUrls } from \"./endpoints.js\";\n\nexport interface HttpClientOptions {\n env: GrvtEnvironment;\n cookie?: string;\n accountId?: string;\n timeoutMs?: number;\n retries?: number;\n backoffMs?: number;\n maxBackoffMs?: number;\n}\n\nexport interface HttpClient {\n post: <T>(baseType: \"edge\" | \"marketData\" | \"trading\", path: string, body?: unknown) => Promise<T>;\n}\n\nexport const createHttpClient = (options: HttpClientOptions): HttpClient => {\n const {\n env,\n cookie,\n accountId,\n timeoutMs = 10000,\n retries = 3,\n backoffMs = 200,\n maxBackoffMs = 10000,\n } = options;\n\n const urls = getBaseUrls(env);\n\n const post = async <T>(baseType: \"edge\" | \"marketData\" | \"trading\", path: string, body?: unknown): Promise<T> => {\n const url = `${urls[baseType]}${path}`;\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n\n if (cookie) {\n headers[\"Cookie\"] = cookie;\n }\n if (accountId) {\n headers[\"X-Grvt-Account-Id\"] = accountId;\n }\n\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timer);\n\n if (response.status === 429 || response.status >= 500) {\n const err = new Error(`HTTP ${response.status}: ${response.statusText}`);\n (err as HttpError).statusCode = response.status;\n throw err;\n }\n\n if (!response.ok) {\n const text = await response.text().catch(() => \"\");\n const err = new Error(`HTTP ${response.status}: ${text || response.statusText}`);\n (err as HttpError).statusCode = response.status;\n throw err;\n }\n\n return (await response.json()) as T;\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n const isRetryable =\n (lastError as HttpError).statusCode === 429 ||\n ((lastError as HttpError).statusCode ?? 0) >= 500 ||\n lastError.name === \"AbortError\";\n\n if (!isRetryable || attempt >= retries) {\n throw lastError;\n }\n\n const delay = Math.min(backoffMs * Math.pow(2, attempt), maxBackoffMs);\n await sleep(delay);\n }\n }\n\n throw lastError ?? new Error(\"Request failed\");\n };\n\n return { post };\n};\n\nexport interface HttpError extends Error {\n statusCode?: number;\n}\n\nexport const extractAuthFromResponse = async (\n env: GrvtEnvironment,\n apiKey: string,\n): Promise<{ cookie: string; accountId: string }> => {\n const urls = getBaseUrls(env);\n const url = `${urls.edge}/auth/api_key/login`;\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Cookie: \"rm=true;\",\n },\n body: JSON.stringify({ api_key: apiKey }),\n redirect: \"manual\",\n });\n\n const bodyText = await response.text().catch(() => \"\");\n\n if (!response.ok && response.status !== 302) {\n throw new Error(`Auth failed (HTTP ${response.status}): ${bodyText || response.statusText}`);\n }\n\n // GRVT returns 200 with error body on auth failure\n if (bodyText) {\n try {\n const bodyJson = JSON.parse(bodyText) as Record<string, unknown>;\n if (bodyJson[\"status\"] === \"failure\" || bodyJson[\"error\"]) {\n throw new Error(`Auth failed: ${bodyJson[\"error\"] ?? bodyJson[\"message\"] ?? \"unknown error\"}`);\n }\n } catch (e) {\n if (e instanceof Error && e.message.startsWith(\"Auth failed\")) throw e;\n // not JSON or no error field, continue\n }\n }\n\n // Extract cookie from set-cookie header\n const setCookies = response.headers.getSetCookie?.() ?? [];\n let gravityCookie = setCookies\n .map((c) => c.split(\";\")[0])\n .find((c) => c?.startsWith(\"gravity=\"));\n\n // Fallback: try the raw set-cookie header value\n if (!gravityCookie) {\n const rawSetCookie = response.headers.get(\"set-cookie\");\n if (rawSetCookie) {\n const match = rawSetCookie.match(/gravity=[^;]+/);\n if (match) {\n gravityCookie = match[0];\n }\n }\n }\n\n if (!gravityCookie) {\n throw new Error(\"Auth response did not contain gravity session cookie. Verify your API key is valid.\");\n }\n\n const accountId = response.headers.get(\"x-grvt-account-id\");\n if (!accountId) {\n throw new Error(\"Auth response did not contain X-Grvt-Account-Id header\");\n }\n\n return { cookie: gravityCookie, accountId: accountId.trim() };\n};\n\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n","import { extractAuthFromResponse } from \"../client/http.js\";\nimport { loadConfig, saveConfig } from \"../config/store.js\";\nimport type { GrvtEnvironment } from \"../client/endpoints.js\";\n\nexport interface LoginOptions {\n apiKey: string;\n env?: GrvtEnvironment;\n privateKey?: string;\n}\n\nexport const performLogin = async (options: LoginOptions): Promise<{ accountId: string; env: GrvtEnvironment }> => {\n const config = loadConfig();\n const env = options.env ?? config.env;\n\n const { cookie, accountId } = await extractAuthFromResponse(env, options.apiKey);\n\n saveConfig({\n ...config,\n env,\n apiKey: options.apiKey,\n cookie,\n accountId,\n ...(options.privateKey ? { privateKey: options.privateKey } : {}),\n });\n\n return { accountId, env };\n};\n","import { loadConfig, saveConfig } from \"../config/store.js\";\nimport { createHttpClient } from \"../client/http.js\";\nimport { ENDPOINTS } from \"../client/endpoints.js\";\nimport { performLogin, type LoginOptions } from \"./login.js\";\n\nexport const login = async (options: LoginOptions) => performLogin(options);\n\nexport const logout = (): void => {\n const config = loadConfig();\n saveConfig({\n ...config,\n apiKey: undefined,\n privateKey: undefined,\n cookie: undefined,\n accountId: undefined,\n });\n};\n\nexport const verifySession = async (): Promise<{ valid: boolean; env: string; accountId?: string }> => {\n const config = loadConfig();\n\n if (!config.cookie || !config.accountId) {\n return { valid: false, env: config.env };\n }\n\n try {\n const client = createHttpClient({\n env: config.env,\n cookie: config.cookie,\n accountId: config.accountId,\n timeoutMs: config.http.timeoutMs,\n });\n\n await client.post(\"trading\", ENDPOINTS.account.fundingAccountSummary, {});\n return { valid: true, env: config.env, accountId: config.accountId };\n } catch {\n return { valid: false, env: config.env, accountId: config.accountId };\n }\n};\n\nexport const getAuthenticatedClient = () => {\n const config = loadConfig();\n\n if (!config.cookie || !config.accountId) {\n throw new Error(\"Not authenticated. Run `grvt auth login` first.\");\n }\n\n return {\n client: createHttpClient({\n env: config.env,\n cookie: config.cookie,\n accountId: config.accountId,\n timeoutMs: config.http.timeoutMs,\n retries: config.http.retries,\n backoffMs: config.http.backoffMs,\n maxBackoffMs: config.http.maxBackoffMs,\n }),\n config,\n };\n};\n","import { Command } from \"commander\";\nimport { randomBytes } from \"node:crypto\";\nimport { ENDPOINTS } from \"../../core/client/endpoints.js\";\nimport { wrapAction, output, resolveSubAccountId } from \"../../core/command-helpers.js\";\nimport { paginateCursor } from \"../../core/pagination/cursor.js\";\nimport { confirm } from \"../../core/safety/confirm.js\";\nimport { parseJsonInput, parseTimestamp } from \"../../core/safety/validate.js\";\nimport { exitUsage, exitAuth } from \"../../core/output/errors.js\";\nimport { logInfo, printOutput } from \"../../core/output/format.js\";\nimport { getInstrument } from \"../../core/instruments/cache.js\";\nimport { buildOrderTypedData, generateNonce, generateExpiration } from \"../../core/signing/eip712.js\";\nimport { signTypedData } from \"../../core/signing/signer.js\";\nimport type {\n CreateOrderRequest,\n CancelOrderRequest,\n CancelAllOrdersRequest,\n GetOrderRequest,\n GetOpenOrdersRequest,\n PaginatedRequest,\n SignedOrder,\n OrderLeg,\n TimeInForce,\n Kind,\n ApiResponse,\n} from \"../../core/client/types.js\";\nimport type { HttpClient } from \"../../core/client/http.js\";\n\n// GRVT requires numeric client_order_id; client range is [2^63, 2^64-1]\nconst generateClientOrderId = (): string => {\n const min = 2n ** 63n;\n const range = 2n ** 63n;\n const rand = BigInt(`0x${randomBytes(8).toString(\"hex\")}`) % range;\n return (min + rand).toString();\n};\n\nexport const createOrder = async (\n client: HttpClient,\n order: SignedOrder,\n): Promise<unknown> => {\n const body: CreateOrderRequest = { order };\n const result = await client.post<ApiResponse>(\"trading\", ENDPOINTS.trading.createOrder, body);\n return result.result;\n};\n\nexport const cancelOrder = async (\n client: HttpClient,\n params: CancelOrderRequest,\n): Promise<unknown> => {\n const result = await client.post<ApiResponse>(\"trading\", ENDPOINTS.trading.cancelOrder, params);\n return result.result;\n};\n\nexport const cancelAllOrders = async (\n client: HttpClient,\n params: CancelAllOrdersRequest,\n): Promise<unknown> => {\n const result = await client.post<ApiResponse>(\"trading\", ENDPOINTS.trading.cancelAllOrders, params);\n return result.result;\n};\n\nexport const getOrder = async (\n client: HttpClient,\n params: GetOrderRequest,\n): Promise<unknown> => {\n const result = await client.post<ApiResponse>(\"trading\", ENDPOINTS.trading.getOrder, params);\n return result.result;\n};\n\nexport const getOpenOrders = async (\n client: HttpClient,\n params: GetOpenOrdersRequest,\n): Promise<unknown> => {\n const result = await client.post<ApiResponse>(\"trading\", ENDPOINTS.trading.openOrders, params);\n return result.result;\n};\n\nexport const getOrderHistory = async (\n client: HttpClient,\n params: PaginatedRequest,\n): Promise<ApiResponse<unknown[]>> => {\n return client.post<ApiResponse<unknown[]>>(\"trading\", ENDPOINTS.trading.orderHistory, params);\n};\n\nexport const registerOrderCommands = (parent: Command) => {\n const orderCmd = parent.command(\"order\").description(\"Order lifecycle management\");\n\n orderCmd\n .command(\"create\")\n .description(\"Create a new order\")\n .option(\"--sub-account-id <id>\", \"sub-account ID\")\n .requiredOption(\"--instrument <name>\", \"instrument symbol\")\n .requiredOption(\"--side <side>\", \"order side: buy|sell\")\n .requiredOption(\"--type <type>\", \"order type: market|limit\")\n .requiredOption(\"--qty <amount>\", \"order quantity\")\n .option(\"--price <price>\", \"limit price (required for limit orders)\")\n .option(\"--time-in-force <tif>\", \"GTT|IOC|AON|FOK\", \"GOOD_TILL_TIME\")\n .option(\"--reduce-only\", \"reduce-only order\")\n .option(\"--post-only\", \"post-only order\")\n .option(\"--client-order-id <id>\", \"client order ID (auto-generated if not set)\")\n .option(\"--expiration-seconds <n>\", \"order expiration in seconds\", \"3600\")\n .option(\"--json <path>\", \"read full request body from file (@path) or stdin (-)\")\n .option(\"--dry-run\", \"validate and show payload without sending\")\n .action(wrapAction(true, async (ctx, opts) => {\n let orderPayload: Record<string, unknown>;\n\n if (opts[\"json\"]) {\n orderPayload = parseJsonInput(opts[\"json\"] as string) as Record<string, unknown>;\n } else {\n const subAccountId = resolveSubAccountId(opts, ctx.config);\n const instrument = opts[\"instrument\"] as string;\n const side = (opts[\"side\"] as string).toUpperCase();\n const type = (opts[\"type\"] as string).toLowerCase();\n const qty = opts[\"qty\"] as string;\n const price = opts[\"price\"] as string | undefined;\n\n if (type === \"limit\" && !price) {\n exitUsage(\"--price is required for limit orders\");\n }\n\n if (!ctx.config.privateKey) {\n exitAuth(\"Private key required for order signing. Run `grvt config set privateKey <key>` or `grvt auth login --private-key <key>`.\");\n }\n\n const instrumentMeta = await getInstrument(ctx.config.env, instrument, ctx.config.cookie, ctx.config.accountId);\n\n const isMarket = type === \"market\";\n\n const leg: OrderLeg = {\n instrument,\n size: qty,\n limit_price: isMarket ? \"0\" : (price ?? \"0\"),\n is_buying_asset: side === \"BUY\",\n };\n\n const tifMap: Record<string, TimeInForce> = {\n GTT: \"GOOD_TILL_TIME\",\n IOC: \"IMMEDIATE_OR_CANCEL\",\n AON: \"ALL_OR_NONE\",\n FOK: \"FILL_OR_KILL\",\n GOOD_TILL_TIME: \"GOOD_TILL_TIME\",\n IMMEDIATE_OR_CANCEL: \"IMMEDIATE_OR_CANCEL\",\n ALL_OR_NONE: \"ALL_OR_NONE\",\n FILL_OR_KILL: \"FILL_OR_KILL\",\n };\n\n const tifInput = (opts[\"timeInForce\"] as string) ?? \"GOOD_TILL_TIME\";\n const timeInForce = tifMap[tifInput.toUpperCase()];\n if (!timeInForce) {\n return exitUsage(`Invalid time-in-force: ${tifInput}. Use GTT, IOC, AON, or FOK.`);\n }\n\n const nonce = generateNonce();\n const expiration = generateExpiration(Number(opts[\"expirationSeconds\"] ?? 3600));\n const clientOrderId = (opts[\"clientOrderId\"] as string) ?? generateClientOrderId();\n\n const typedData = buildOrderTypedData({\n subAccountId,\n isMarket,\n timeInForce,\n postOnly: Boolean(opts[\"postOnly\"]),\n reduceOnly: Boolean(opts[\"reduceOnly\"]),\n legs: [leg],\n instruments: {\n [instrument]: {\n instrument_hash: instrumentMeta.instrument_hash as string,\n base_decimals: (instrumentMeta.base_decimals as number) ?? 9,\n },\n },\n expiration,\n nonce,\n }, ctx.config.env);\n\n const sigComponents = await signTypedData(ctx.config.privateKey!, typedData);\n\n orderPayload = {\n order: {\n sub_account_id: subAccountId,\n is_market: isMarket,\n time_in_force: timeInForce,\n post_only: Boolean(opts[\"postOnly\"]),\n reduce_only: Boolean(opts[\"reduceOnly\"]),\n legs: [leg],\n signature: {\n signer: sigComponents.signer,\n r: sigComponents.r,\n s: sigComponents.s,\n v: sigComponents.v,\n expiration,\n nonce,\n },\n metadata: { client_order_id: clientOrderId },\n },\n };\n }\n\n if (opts[\"dryRun\"]) {\n logInfo(\"Dry run - payload that would be sent:\", Boolean(ctx.globalOpts[\"silent\"]));\n printOutput(orderPayload, { ...ctx.outputOptions, output: \"json\", pretty: true });\n return;\n }\n\n const result = await ctx.client.post<ApiResponse>(\"trading\", ENDPOINTS.trading.createOrder, orderPayload);\n output(result.result, ctx.outputOptions);\n }));\n\n orderCmd\n .command(\"get\")\n .description(\"Get an order by ID\")\n .option(\"--sub-account-id <id>\", \"sub-account ID\")\n .option(\"--order-id <id>\", \"order ID\")\n .option(\"--client-order-id <id>\", \"client order ID\")\n .action(wrapAction(true, async (ctx, opts) => {\n const subAccountId = resolveSubAccountId(opts, ctx.config);\n if (!opts[\"orderId\"] && !opts[\"clientOrderId\"]) {\n exitUsage(\"Either --order-id or --client-order-id is required\");\n }\n const body: GetOrderRequest = {\n sub_account_id: subAccountId,\n ...(opts[\"orderId\"] ? { order_id: opts[\"orderId\"] as string } : {} as Record<string, never>),\n ...(opts[\"clientOrderId\"] ? { client_order_id: opts[\"clientOrderId\"] as string } : {} as Record<string, never>),\n };\n const result = await getOrder(ctx.client, body);\n output(result, ctx.outputOptions);\n }));\n\n orderCmd\n .command(\"open\")\n .description(\"List open orders\")\n .option(\"--sub-account-id <id>\", \"sub-account ID\")\n .option(\"--kind <kinds>\", \"filter by kind (comma-separated)\")\n .option(\"--base <currencies>\", \"filter by base currency\")\n .option(\"--quote <currencies>\", \"filter by quote currency\")\n .action(wrapAction(true, async (ctx, opts) => {\n const subAccountId = resolveSubAccountId(opts, ctx.config);\n const body: GetOpenOrdersRequest = {\n sub_account_id: subAccountId,\n ...(opts[\"kind\"] ? { kind: (opts[\"kind\"] as string).split(\",\") as Kind[] } : {} as Record<string, never>),\n ...(opts[\"base\"] ? { base: (opts[\"base\"] as string).split(\",\") } : {} as Record<string, never>),\n ...(opts[\"quote\"] ? { quote: (opts[\"quote\"] as string).split(\",\") } : {} as Record<string, never>),\n };\n const result = await getOpenOrders(ctx.client, body);\n output(result, ctx.outputOptions);\n }));\n\n orderCmd\n .command(\"cancel\")\n .description(\"Cancel a single order\")\n .option(\"--sub-account-id <id>\", \"sub-account ID\")\n .option(\"--order-id <id>\", \"order ID\")\n .option(\"--client-order-id <id>\", \"client order ID\")\n .option(\"--dry-run\", \"validate and show payload without sending\")\n .action(wrapAction(true, async (ctx, opts) => {\n const subAccountId = resolveSubAccountId(opts, ctx.config);\n if (!opts[\"orderId\"] && !opts[\"clientOrderId\"]) {\n exitUsage(\"Either --order-id or --client-order-id is required\");\n }\n const body: CancelOrderRequest = {\n sub_account_id: subAccountId,\n ...(opts[\"orderId\"] ? { order_id: opts[\"orderId\"] as string } : {} as Record<string, never>),\n ...(opts[\"clientOrderId\"] ? { client_order_id: opts[\"clientOrderId\"] as string } : {} as Record<string, never>),\n };\n\n if (opts[\"dryRun\"]) {\n logInfo(\"Dry run - payload that would be sent:\", Boolean(ctx.globalOpts[\"silent\"]));\n printOutput(body, { ...ctx.outputOptions, output: \"json\", pretty: true });\n return;\n }\n\n const result = await cancelOrder(ctx.client, body);\n output(result, ctx.outputOptions);\n }));\n\n orderCmd\n .command(\"cancel-all\")\n .description(\"Cancel all open orders\")\n .option(\"--sub-account-id <id>\", \"sub-account ID\")\n .option(\"--instrument <name>\", \"filter by instrument\")\n .option(\"--dry-run\", \"validate and show payload without sending\")\n .action(wrapAction(true, async (ctx, opts) => {\n const subAccountId = resolveSubAccountId(opts, ctx.config);\n const body: CancelAllOrdersRequest = {\n sub_account_id: subAccountId,\n ...(opts[\"instrument\"] ? { instrument: opts[\"instrument\"] as string } : {} as Record<string, never>),\n };\n\n if (opts[\"dryRun\"]) {\n logInfo(\"Dry run - payload that would be sent:\", Boolean(ctx.globalOpts[\"silent\"]));\n printOutput(body, { ...ctx.outputOptions, output: \"json\", pretty: true });\n return;\n }\n\n const skipConfirm = Boolean(ctx.globalOpts[\"yes\"]);\n const ok = await confirm(\"Cancel ALL open orders?\", skipConfirm);\n if (!ok) {\n logInfo(\"Aborted.\", Boolean(ctx.globalOpts[\"silent\"]));\n return;\n }\n\n const result = await cancelAllOrders(ctx.client, body);\n output(result, ctx.outputOptions);\n }));\n\n orderCmd\n .command(\"history\")\n .description(\"Get order history\")\n .option(\"--sub-account-id <id>\", \"sub-account ID\")\n .option(\"--kind <kinds>\", \"filter by kind (comma-separated)\")\n .option(\"--base <currencies>\", \"filter by base currency\")\n .option(\"--quote <currencies>\", \"filter by quote currency\")\n .option(\"--start-time <time>\", \"start time\")\n .option(\"--end-time <time>\", \"end time\")\n .option(\"--limit <n>\", \"max results\")\n .option(\"--cursor <cursor>\", \"pagination cursor\")\n .option(\"--all\", \"auto-paginate all results\")\n .action(wrapAction(true, async (ctx, opts) => {\n const subAccountId = resolveSubAccountId(opts, ctx.config);\n\n const results = await paginateCursor({\n fetchPage: async (cursor) => {\n const body: PaginatedRequest = {\n sub_account_id: subAccountId,\n ...(opts[\"kind\"] ? { kind: (opts[\"kind\"] as string).split(\",\") as Kind[] } : {} as Record<string, never>),\n ...(opts[\"base\"] ? { base: (opts[\"base\"] as string).split(\",\") } : {} as Record<string, never>),\n ...(opts[\"quote\"] ? { quote: (opts[\"quote\"] as string).split(\",\") } : {} as Record<string, never>),\n ...(opts[\"startTime\"] ? { start_time: parseTimestamp(opts[\"startTime\"] as string) } : {} as Record<string, never>),\n ...(opts[\"endTime\"] ? { end_time: parseTimestamp(opts[\"endTime\"] as string) } : {} as Record<string, never>),\n ...(opts[\"limit\"] ? { limit: Number(opts[\"limit\"]) } : {} as Record<string, never>),\n ...(cursor ? { cursor } : {} as Record<string, never>),\n };\n const resp = await getOrderHistory(ctx.client, body);\n return { result: resp.result ?? [], next: resp.next };\n },\n cursor: opts[\"cursor\"] as string | undefined,\n all: Boolean(opts[\"all\"]),\n outputOptions: ctx.outputOptions,\n });\n\n if (ctx.outputOptions.output !== \"ndjson\" || !opts[\"all\"]) {\n output(results, ctx.outputOptions);\n }\n }));\n};\n","import Table from \"cli-table3\";\nimport { c } from \"./colors.js\";\n\nexport const renderTable = (data: Record<string, unknown>[]): string => {\n if (data.length === 0) return \"No data\";\n\n const keys = Object.keys(data[0]!);\n const table = new Table({\n head: keys.map((k) => c().cyan.bold(k)),\n style: { head: [], border: [] },\n });\n\n for (const row of data) {\n table.push(keys.map((k) => formatCell(row[k])));\n }\n\n return table.toString();\n};\n\nexport const renderVerticalTable = (data: Record<string, unknown>): string => {\n const table = new Table({\n style: { head: [], border: [] },\n });\n\n for (const [key, value] of Object.entries(data)) {\n table.push({ [c().cyan.bold(key)]: formatCell(value) });\n }\n\n return table.toString();\n};\n\nconst formatCell = (value: unknown): string => {\n if (value === null || value === undefined) return \"\";\n if (typeof value === \"object\") return JSON.stringify(value);\n return String(value);\n};\n","import chalk from \"chalk\";\n\nlet _disabled = false;\n\nexport const disableColors = () => {\n _disabled = true;\n};\n\nexport const c = () => {\n if (_disabled) {\n return {\n red: (s: string) => s,\n green: (s: string) => s,\n yellow: (s: string) => s,\n cyan: { bold: (s: string) => s },\n } as unknown as typeof chalk;\n }\n return chalk;\n};\n","import { renderTable, renderVerticalTable } from \"./table.js\";\n\nexport type OutputFormat = \"json\" | \"ndjson\" | \"table\" | \"raw\";\n\nexport interface OutputOptions {\n output: OutputFormat;\n pretty?: boolean;\n silent?: boolean;\n}\n\nexport const resolveOutputOptions = (opts: Record<string, unknown>): OutputOptions => {\n const isTTY = process.stdout.isTTY ?? false;\n const format = (opts[\"output\"] as OutputFormat) ?? (isTTY ? \"table\" : \"json\");\n return {\n output: format,\n pretty: Boolean(opts[\"pretty\"]),\n silent: Boolean(opts[\"silent\"]),\n };\n};\n\nexport const formatOutput = (data: unknown, options: OutputOptions): string => {\n switch (options.output) {\n case \"json\":\n return options.pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);\n\n case \"ndjson\":\n if (Array.isArray(data)) {\n return data.map((item) => JSON.stringify(item)).join(\"\\n\");\n }\n return JSON.stringify(data);\n\n case \"raw\":\n if (typeof data === \"string\") return data;\n return JSON.stringify(data);\n\n case \"table\":\n if (Array.isArray(data)) {\n if (data.length === 0) return \"No data\";\n if (typeof data[0] === \"object\" && data[0] !== null) {\n return renderTable(data as Record<string, unknown>[]);\n }\n return data.map(String).join(\"\\n\");\n }\n if (typeof data === \"object\" && data !== null) {\n return renderVerticalTable(data as Record<string, unknown>);\n }\n return String(data);\n }\n};\n\nexport const printOutput = (data: unknown, options: OutputOptions): void => {\n const formatted = formatOutput(data, options);\n process.stdout.write(formatted + \"\\n\");\n};\n\nexport const logInfo = (message: string, silent?: boolean): void => {\n if (!silent) {\n process.stderr.write(message + \"\\n\");\n }\n};\n","import type { OutputOptions } from \"../output/format.js\";\nimport { formatOutput } from \"../output/format.js\";\n\nexport interface PaginateOptions<T> {\n fetchPage: (cursor?: string) => Promise<{ result: T[]; next?: string }>;\n limit?: number;\n cursor?: string;\n all?: boolean;\n outputOptions: OutputOptions;\n}\n\nexport const paginateCursor = async <T>(options: PaginateOptions<T>): Promise<T[]> => {\n const { fetchPage, all, outputOptions } = options;\n let { cursor } = options;\n\n if (!all) {\n const page = await fetchPage(cursor);\n return page.result;\n }\n\n const isStreaming = outputOptions.output === \"ndjson\";\n const collected: T[] = [];\n\n while (true) {\n const page = await fetchPage(cursor);\n\n if (isStreaming) {\n for (const item of page.result) {\n process.stdout.write(formatOutput(item, outputOptions) + \"\\n\");\n }\n } else {\n collected.push(...page.result);\n }\n\n if (!page.next || page.result.length === 0) {\n break;\n }\n\n cursor = page.next;\n }\n\n return collected;\n};\n","import { createInterface } from \"node:readline\";\nimport { c } from \"../output/colors.js\";\n\nexport const confirm = async (message: string, skipConfirm?: boolean): Promise<boolean> => {\n if (skipConfirm) return true;\n\n const rl = createInterface({ input: process.stdin, output: process.stderr });\n\n return new Promise((resolve) => {\n rl.question(c().yellow(`${message} [y/N] `), (answer) => {\n rl.close();\n resolve(answer.trim().toLowerCase() === \"y\");\n });\n });\n};\n","import { readFileSync, readSync } from \"node:fs\";\nimport { exitUsage } from \"../output/errors.js\";\n\nexport const requireOption = (value: unknown, name: string): string => {\n if (value === undefined || value === null || value === \"\") {\n return exitUsage(`--${name} is required`);\n }\n return String(value);\n};\n\nexport const parseJsonInput = (jsonPath: string): unknown => {\n let raw: string;\n\n if (jsonPath === \"-\") {\n raw = readStdin();\n } else {\n const filePath = jsonPath.startsWith(\"@\") ? jsonPath.slice(1) : jsonPath;\n try {\n raw = readFileSync(filePath, \"utf-8\");\n } catch (error) {\n return exitUsage(`Cannot read file: ${filePath} (${error instanceof Error ? error.message : String(error)})`);\n }\n }\n\n try {\n return JSON.parse(raw);\n } catch {\n return exitUsage(\"Invalid JSON input\");\n }\n};\n\nconst readStdin = (): string => {\n const chunks: Buffer[] = [];\n const fd = 0;\n const buf = Buffer.alloc(4096);\n let bytesRead: number;\n\n try {\n while ((bytesRead = readSync(fd, buf, 0, buf.length, null)) > 0) {\n chunks.push(buf.subarray(0, bytesRead));\n }\n } catch {\n // EOF or broken pipe\n }\n\n return Buffer.concat(chunks).toString(\"utf-8\");\n};\n\nexport const parseTimestamp = (value: string): string => {\n const num = Number(value);\n if (!Number.isNaN(num)) {\n if (num < 1e12) {\n return String(BigInt(num) * 1000000000n);\n }\n if (num < 1e15) {\n return String(BigInt(num) * 1000000n);\n }\n return String(BigInt(num));\n }\n\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) {\n return exitUsage(`Invalid timestamp: ${value}`);\n }\n return String(BigInt(date.getTime()) * 1000000n);\n};\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { createHttpClient } from \"../client/http.js\";\nimport { ENDPOINTS, type GrvtEnvironment } from \"../client/endpoints.js\";\n\nexport interface InstrumentMetadata {\n instrument: string;\n instrument_hash: string;\n base: string;\n quote: string;\n kind: string;\n venues: string[];\n tick_size: string;\n min_size: string;\n [key: string]: unknown;\n}\n\ninterface InstrumentCache {\n env: string;\n fetched_at: number;\n instruments: Record<string, InstrumentMetadata>;\n}\n\nconst CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour\n\nconst getCacheDir = (): string => {\n const xdgCache = process.env[\"XDG_CACHE_HOME\"];\n const base = xdgCache || join(homedir(), \".cache\");\n return join(base, \"grvt\");\n};\n\nconst getCachePath = (env: GrvtEnvironment): string =>\n join(getCacheDir(), `instruments-${env}.json`);\n\nconst loadCache = (env: GrvtEnvironment): InstrumentCache | null => {\n const path = getCachePath(env);\n if (!existsSync(path)) return null;\n\n try {\n const raw = readFileSync(path, \"utf-8\");\n const cache = JSON.parse(raw) as InstrumentCache;\n\n if (cache.env !== env || Date.now() - cache.fetched_at > CACHE_TTL_MS) {\n return null;\n }\n\n return cache;\n } catch {\n return null;\n }\n};\n\nconst saveCache = (env: GrvtEnvironment, instruments: Record<string, InstrumentMetadata>): void => {\n const dir = getCacheDir();\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n const cache: InstrumentCache = {\n env,\n fetched_at: Date.now(),\n instruments,\n };\n\n writeFileSync(getCachePath(env), JSON.stringify(cache), \"utf-8\");\n};\n\nexport const getInstruments = async (env: GrvtEnvironment, cookie?: string, accountId?: string): Promise<Record<string, InstrumentMetadata>> => {\n const cached = loadCache(env);\n if (cached) return cached.instruments;\n\n const client = createHttpClient({ env, cookie, accountId });\n const response = await client.post<{ result: InstrumentMetadata[] }>(\n \"marketData\",\n ENDPOINTS.marketData.allInstruments,\n { is_active: true },\n );\n\n const instruments: Record<string, InstrumentMetadata> = {};\n for (const inst of response.result ?? []) {\n instruments[inst.instrument] = inst;\n }\n\n saveCache(env, instruments);\n return instruments;\n};\n\nexport const getInstrument = async (\n env: GrvtEnvironment,\n instrument: string,\n cookie?: string,\n accountId?: string,\n): Promise<InstrumentMetadata> => {\n const instruments = await getInstruments(env, cookie, accountId);\n const meta = instruments[instrument];\n if (!meta) {\n throw new Error(`Instrument not found: ${instrument}. Run 'grvt market instruments' to see available instruments.`);\n }\n return meta;\n};\n","import type { GrvtEnvironment } from \"../client/endpoints.js\";\nimport { getChainIds } from \"../client/endpoints.js\";\nimport type { OrderLeg, TimeInForce } from \"../client/types.js\";\n\nexport interface InstrumentMeta {\n instrument_hash: string;\n base_decimals: number;\n}\n\nexport interface OrderPayload {\n subAccountId: string;\n isMarket: boolean;\n timeInForce: TimeInForce;\n postOnly: boolean;\n reduceOnly: boolean;\n legs: OrderLeg[];\n instruments: Record<string, InstrumentMeta>;\n expiration: string;\n nonce: number;\n}\n\nconst PRICE_MULTIPLIER = 1_000_000_000n;\n\nconst TIME_IN_FORCE_MAP: Record<TimeInForce, number> = {\n GOOD_TILL_TIME: 1,\n ALL_OR_NONE: 2,\n IMMEDIATE_OR_CANCEL: 3,\n FILL_OR_KILL: 4,\n};\n\nconst decimalToBigInt = (value: string, multiplier: bigint): bigint => {\n const parts = value.split(\".\");\n const whole = parts[0] ?? \"0\";\n const frac = parts[1] ?? \"\";\n const multiplierDigits = multiplier.toString().length - 1;\n const paddedFrac = frac.padEnd(multiplierDigits, \"0\").slice(0, multiplierDigits);\n return BigInt(whole) * multiplier + BigInt(paddedFrac);\n};\n\nexport const buildOrderTypedData = (payload: OrderPayload, env: GrvtEnvironment) => {\n const chainIds = getChainIds(env);\n\n const domain = {\n name: \"GRVT Exchange\",\n version: \"0\",\n chainId: BigInt(chainIds.l2),\n } as const;\n\n const types = {\n Order: [\n { name: \"subAccountID\", type: \"uint64\" },\n { name: \"isMarket\", type: \"bool\" },\n { name: \"timeInForce\", type: \"uint8\" },\n { name: \"postOnly\", type: \"bool\" },\n { name: \"reduceOnly\", type: \"bool\" },\n { name: \"legs\", type: \"OrderLeg[]\" },\n { name: \"nonce\", type: \"uint32\" },\n { name: \"expiration\", type: \"int64\" },\n ],\n OrderLeg: [\n { name: \"assetID\", type: \"uint256\" },\n { name: \"contractSize\", type: \"uint64\" },\n { name: \"limitPrice\", type: \"uint64\" },\n { name: \"isBuyingContract\", type: \"bool\" },\n ],\n } as const;\n\n const legs = payload.legs.map((leg) => {\n const meta = payload.instruments[leg.instrument];\n if (!meta) {\n throw new Error(`Instrument metadata not found for: ${leg.instrument}`);\n }\n const sizeMultiplier = BigInt(10) ** BigInt(meta.base_decimals);\n return {\n assetID: BigInt(meta.instrument_hash),\n contractSize: decimalToBigInt(leg.size, sizeMultiplier),\n limitPrice: decimalToBigInt(leg.limit_price ?? \"0\", PRICE_MULTIPLIER),\n isBuyingContract: leg.is_buying_asset,\n };\n });\n\n const message = {\n subAccountID: BigInt(payload.subAccountId),\n isMarket: payload.isMarket,\n timeInForce: TIME_IN_FORCE_MAP[payload.timeInForce],\n postOnly: payload.postOnly,\n reduceOnly: payload.reduceOnly,\n legs,\n nonce: payload.nonce,\n expiration: BigInt(payload.expiration),\n };\n\n return { domain, types, primaryType: \"Order\" as const, message };\n};\n\n// ---- Transfer EIP-712 ----\n\nexport interface TransferPayload {\n fromAccount: string;\n fromSubAccount: string;\n toAccount: string;\n toSubAccount: string;\n tokenCurrency: number;\n numTokens: string;\n balanceDecimals: number;\n expiration: string;\n nonce: number;\n}\n\nexport const buildTransferTypedData = (payload: TransferPayload, env: GrvtEnvironment) => {\n const chainIds = getChainIds(env);\n\n const domain = {\n name: \"GRVT Exchange\",\n version: \"0\",\n chainId: BigInt(chainIds.l2),\n } as const;\n\n const types = {\n Transfer: [\n { name: \"fromAccount\", type: \"address\" },\n { name: \"fromSubAccount\", type: \"uint64\" },\n { name: \"toAccount\", type: \"address\" },\n { name: \"toSubAccount\", type: \"uint64\" },\n { name: \"tokenCurrency\", type: \"uint8\" },\n { name: \"numTokens\", type: \"uint64\" },\n { name: \"nonce\", type: \"uint32\" },\n { name: \"expiration\", type: \"int64\" },\n ],\n } as const;\n\n const tokenMultiplier = BigInt(10) ** BigInt(payload.balanceDecimals);\n\n const message = {\n fromAccount: payload.fromAccount as `0x${string}`,\n fromSubAccount: BigInt(payload.fromSubAccount),\n toAccount: payload.toAccount as `0x${string}`,\n toSubAccount: BigInt(payload.toSubAccount),\n tokenCurrency: payload.tokenCurrency,\n numTokens: decimalToBigInt(payload.numTokens, tokenMultiplier),\n nonce: payload.nonce,\n expiration: BigInt(payload.expiration),\n };\n\n return { domain, types, primaryType: \"Transfer\" as const, message };\n};\n\n// ---- Withdrawal EIP-712 ----\n\nexport interface WithdrawalPayload {\n fromAccount: string;\n toEthAddress: string;\n tokenCurrency: number;\n numTokens: string;\n balanceDecimals: number;\n expiration: string;\n nonce: number;\n}\n\nexport const buildWithdrawalTypedData = (payload: WithdrawalPayload, env: GrvtEnvironment) => {\n const chainIds = getChainIds(env);\n\n const domain = {\n name: \"GRVT Exchange\",\n version: \"0\",\n chainId: BigInt(chainIds.l2),\n } as const;\n\n const types = {\n Withdrawal: [\n { name: \"fromAccount\", type: \"address\" },\n { name: \"toEthAddress\", type: \"address\" },\n { name: \"tokenCurrency\", type: \"uint8\" },\n { name: \"numTokens\", type: \"uint64\" },\n { name: \"nonce\", type: \"uint32\" },\n { name: \"expiration\", type: \"int64\" },\n ],\n } as const;\n\n const tokenMultiplier = BigInt(10) ** BigInt(payload.balanceDecimals);\n\n const message = {\n fromAccount: payload.fromAccount as `0x${string}`,\n toEthAddress: payload.toEthAddress as `0x${string}`,\n tokenCurrency: payload.tokenCurrency,\n numTokens: decimalToBigInt(payload.numTokens, tokenMultiplier),\n nonce: payload.nonce,\n expiration: BigInt(payload.expiration),\n };\n\n return { domain, types, primaryType: \"Withdrawal\" as const, message };\n};\n\n// ---- Derisk EIP-712 ----\n\nexport interface DeriskPayload {\n subAccountId: string;\n ratio: string;\n expiration: string;\n nonce: number;\n}\n\nexport const buildDeriskTypedData = (payload: DeriskPayload, env: GrvtEnvironment) => {\n const chainIds = getChainIds(env);\n\n const domain = {\n name: \"GRVT Exchange\",\n version: \"0\",\n chainId: BigInt(chainIds.l2),\n } as const;\n\n const types = {\n SetDeriskMMRatio: [\n { name: \"subAccountID\", type: \"uint64\" },\n { name: \"ratio\", type: \"uint64\" },\n { name: \"nonce\", type: \"uint32\" },\n { name: \"expiration\", type: \"int64\" },\n ],\n } as const;\n\n const message = {\n subAccountID: BigInt(payload.subAccountId),\n ratio: decimalToBigInt(payload.ratio, PRICE_MULTIPLIER),\n nonce: payload.nonce,\n expiration: BigInt(payload.expiration),\n };\n\n return { domain, types, primaryType: \"SetDeriskMMRatio\" as const, message };\n};\n\n// ---- Shared utilities ----\n\nexport const generateNonce = (): number =>\n Math.floor(Math.random() * 2147483647);\n\nexport const generateExpiration = (durationSeconds: number = 3600): string => {\n const expirationMs = Date.now() + durationSeconds * 1000;\n return String(BigInt(expirationMs) * 1000000n);\n};\n","import { privateKeyToAccount, type PrivateKeyAccount } from \"viem/accounts\";\nimport { parseSignature, type Hex } from \"viem\";\n\nexport interface SignatureComponents {\n signer: string;\n r: string;\n s: string;\n v: number;\n}\n\nexport const createSigner = (privateKey: string): PrivateKeyAccount => {\n const key = privateKey.startsWith(\"0x\") ? privateKey : `0x${privateKey}`;\n return privateKeyToAccount(key as Hex);\n};\n\nexport const signTypedData = async (\n privateKey: string,\n typedData: {\n domain: Record<string, unknown>;\n types: Record<string, ReadonlyArray<{ name: string; type: string }>>;\n primaryType: string;\n message: Record<string, unknown>;\n },\n): Promise<SignatureComponents> => {\n const account = createSigner(privateKey);\n const sig = await account.signTypedData(typedData as Parameters<typeof account.signTypedData>[0]);\n const parsed = parseSignature(sig);\n\n return {\n signer: account.address,\n r: parsed.r,\n s: parsed.s,\n v: Number(parsed.v),\n };\n};\n","import { Command } from \"commander\";\nimport { ENDPOINTS } from \"../../core/client/endpoints.js\";\nimport { wrapAction, output, resolveSubAccountId } from \"../../core/command-helpers.js\";\nimport { paginateCursor } from \"../../core/pagination/cursor.js\";\nimport { parseTimestamp } from \"../../core/safety/validate.js\";\nimport type { PaginatedRequest, ApiResponse, Kind } from \"../../core/client/types.js\";\nimport type { HttpClient } from \"../../core/client/http.js\";\n\nexport const getFillHistory = async (\n client: HttpClient,\n params: PaginatedRequest,\n): Promise<ApiResponse<unknown[]>> => {\n return client.post<ApiResponse<unknown[]>>(\"trading\", ENDPOINTS.trading.fillHistory, params);\n};\n\nexport const registerFillCommands = (parent: Command) => {\n parent\n .command(\"fills\")\n .description(\"Get fill (trade execution) history\")\n .option(\"--sub-account-id <id>\", \"sub-account ID\")\n .option(\"--kind <kinds>\", \"filter by kind (comma-separated)\")\n .option(\"--base <currencies>\", \"filter by base currency\")\n .option(\"--quote <currencies>\", \"filter by quote currency\")\n .option(\"--start-time <time>\", \"start time\")\n .option(\"--end-time <time>\", \"end time\")\n .option(\"--limit <n>\", \"max results\")\n .option(\"--cursor <cursor>\", \"pagination cursor\")\n .option(\"--all\", \"auto-paginate all results\")\n .action(wrapAction(true, async (ctx, opts) => {\n const subAccountId = resolveSubAccountId(opts, ctx.config);\n\n const results = await paginateCursor({\n fetchPage: async (cursor) => {\n const body: PaginatedRequest = {\n sub_account_id: subAccountId,\n ...(opts[\"kind\"] ? { kind: (opts[\"kind\"] as string).split(\",\") as Kind[] } : {} as Record<string, never>),\n ...(opts[\"base\"] ? { base: (opts[\"base\"] as string).split(\",\") } : {} as Record<string, never>),\n ...(opts[\"quote\"] ? { quote: (opts[\"quote\"] as string).split(\",\") } : {} as Record<string, never>),\n ...(opts[\"startTime\"] ? { start_time: parseTimestamp(opts[\"startTime\"] as string) } : {} as Record<string, never>),\n ...(opts[\"endTime\"] ? { end_time: parseTimestamp(opts[\"endTime\"] as string) } : {} as Record<string, never>),\n ...(opts[\"limit\"] ? { limit: Number(opts[\"limit\"]) } : {} as Record<string, never>),\n ...(cursor ? { cursor } : {} as Record<string, never>),\n };\n const resp = await getFillHistory(ctx.client, body);\n return { result: resp.result ?? [], next: resp.next };\n },\n cursor: opts[\"cursor\"] as string | undefined,\n all: Boolean(opts[\"all\"]),\n outputOptions: ctx.outputOptions,\n });\n\n if (ctx.outputOptions.output !== \"ndjson\" || !opts[\"all\"]) {\n output(results, ctx.outputOptions);\n }\n }));\n};\n","import { Command } from \"commander\";\nimport { ENDPOINTS } from \"../../core/client/endpoints.js\";\nimport { wrapAction, output, resolveSubAccountId } from \"../../core/command-helpers.js\";\nimport type { GetPositionsRequest, ApiResponse, Kind } from \"../../core/client/types.js\";\nimport type { HttpClient } from \"../../core/client/http.js\";\n\nexport const getPositions = async (\n client: HttpClient,\n params: GetPositionsRequest,\n): Promise<unknown> => {\n const result = await client.post<ApiResponse>(\"trading\", ENDPOINTS.trading.positions, params);\n return result.result;\n};\n\nexport const registerPositionCommands = (parent: Command) => {\n parent\n .command(\"positions\")\n .description(\"Get current positions\")\n .option(\"--sub-account-id <id>\", \"sub-account ID\")\n .option(\"--kind <kinds>\", \"filter by kind (comma-separated)\")\n .option(\"--base <currencies>\", \"filter by base currency\")\n .option(\"--quote <currencies>\", \"filter by quote currency\")\n .action(wrapAction(true, async (ctx, opts) => {\n const subAccountId = resolveSubAccountId(opts, ctx.config);\n const body: GetPositionsRequest = {\n sub_account_id: subAccountId,\n ...(opts[\"kind\"] ? { kind: (opts[\"kind\"] as string).split(\",\") as Kind[] } : {} as Record<string, never>),\n ...(opts[\"base\"] ? { base: (opts[\"base\"] as string).split(\",\") } : {} as Record<string, never>),\n ...(opts[\"quote\"] ? { quote: (opts[\"quote\"] as string).split(\",\") } : {} as Record<string, never>),\n };\n const result = await getPositions(ctx.client, body);\n output(result, ctx.outputOptions);\n }));\n};\n","import { Command } from \"commander\";\nimport { ENDPOINTS } from \"../../core/client/endpoints.js\";\nimport { wrapAction, output, resolveSubAccountId } from \"../../core/command-helpers.js\";\nimport { paginateCursor } from \"../../core/pagination/cursor.js\";\nimport { parseTimestamp } from \"../../core/safety/validate.js\";\nimport type { PaginatedRequest, ApiResponse, Kind } from \"../../core/client/types.js\";\nimport type { HttpClient } from \"../../core/client/http.js\";\n\nexport const getFundingPayments = async (\n client: HttpClient,\n params: PaginatedRequest,\n): Promise<ApiResponse<unknown[]>> => {\n return client.post<ApiResponse<unknown[]>>(\"trading\", ENDPOINTS.trading.fundingPaymentHistory, params);\n};\n\nexport const registerFundingPaymentCommands = (parent: Command) => {\n parent\n .command(\"funding-payments\")\n .description(\"Get funding payment history\")\n .option(\"--sub-account-id <id>\", \"sub-account ID\")\n .option(\"--kind <kinds>\", \"filter by kind (comma-separated)\")\n .option(\"--base <currencies>\", \"filter by base currency\")\n .option(\"--quote <currencies>\", \"filter by quote currency\")\n .option(\"--start-time <time>\", \"start time\")\n .option(\"--end-time <time>\", \"end time\")\n .option(\"--limit <n>\", \"max results\")\n .option(\"--cursor <cursor>\", \"pagination cursor\")\n .option(\"--all\", \"auto-paginate all results\")\n .action(wrapAction(true, async (ctx, opts) => {\n const subAccountId = resolveSubAccountId(opts, ctx.config);\n\n const results = await paginateCursor({\n fetchPage: async (cursor) => {\n const body: PaginatedRequest = {\n sub_account_id: subAccountId,\n ...(opts[\"kind\"] ? { kind: (opts[\"kind\"] as string).split(\",\") as Kind[] } : {} as Record<string, never>),\n ...(opts[\"base\"] ? { base: (opts[\"base\"] as string).split(\",\") } : {} as Record<string, never>),\n ...(opts[\"quote\"] ? { quote: (opts[\"quote\"] as string).split(\",\") } : {} as Record<string, never>),\n ...(opts[\"startTime\"] ? { start_time: parseTimestamp(opts[\"startTime\"] as string) } : {} as Record<string, never>),\n ...(opts[\"endTime\"] ? { end_time: parseTimestamp(opts[\"endTime\"] as string) } : {} as Record<string, never>),\n ...(opts[\"limit\"] ? { limit: Number(opts[\"limit\"]) } : {} as Record<string, never>),\n ...(cursor ? { cursor } : {} as Record<string, never>),\n };\n const resp = await getFundingPayments(ctx.client, body);\n return { result: resp.result ?? [], next: resp.next };\n },\n cursor: opts[\"cursor\"] as string | undefined,\n all: Boolean(opts[\"all\"]),\n outputOptions: ctx.outputOptions,\n });\n\n if (ctx.outputOptions.output !== \"ndjson\" || !opts[\"all\"]) {\n output(results, ctx.outputOptions);\n }\n }));\n};\n","import { Command } from \"commander\";\nimport { ENDPOINTS } from \"../core/client/endpoints.js\";\nimport { wrapAction, output, resolveSubAccountId } from \"../core/command-helpers.js\";\nimport { paginateCursor } from \"../core/pagination/cursor.js\";\nimport { parseTimestamp } from \"../core/safety/validate.js\";\nimport type { ApiResponse } from \"../core/client/types.js\";\n\nexport const getFundingAccountSummary = async (client: { post: <T>(base: \"edge\" | \"marketData\" | \"trading\", path: string, body?: unknown) => Promise<T> }) => {\n const result = await client.post<ApiResponse>(\"trading\", ENDPOINTS.account.fundingAccountSummary, {});\n return result.result;\n};\n\nexport const getSubAccountSummary = async (\n client: { post: <T>(base: \"edge\" | \"marketData\" | \"trading\", path: string, body?: unknown) => Promise<T> },\n subAccountId: string,\n) => {\n const body = { sub_account_id: subAccountId };\n const result = await client.post<ApiResponse>(\"trading\", ENDPOINTS.account.accountSummary, body);\n return result.result;\n};\n\nexport const getSubAccountHistory = getSubAccountSummary;\n\nexport const getAccountHistory = async (\n client: { post: <T>(base: \"edge\" | \"marketData\" | \"trading\", path: string, body?: unknown) => Promise<T> },\n params: { sub_account_id: string; start_time?: string; end_time?: string; limit?: number; cursor?: string },\n): Promise<ApiResponse<unknown[]>> => {\n return client.post<ApiResponse<unknown[]>>(\"trading\", ENDPOINTS.account.accountHistory, params);\n};\n\nexport const getAggregatedAccountSummary = async (\n client: { post: <T>(base: \"edge\" | \"marketData\" | \"trading\", path: string, body?: unknown) => Promise<T> },\n) => {\n const result = await client.post<ApiResponse>(\"trading\", ENDPOINTS.account.aggregatedAccountSummary, {});\n return result.result;\n};\n\nexport const registerAccountCommands = (program: Command) => {\n const accountCmd = program.command(\"account\").description(\"Account summaries and sub-account info\");\n\n accountCmd\n .command(\"funding\")\n .description(\"Get funding account summary\")\n .action(wrapAction(true, async (ctx) => {\n const result = await getFundingAccountSummary(ctx.client);\n output(result, ctx.outputOptions);\n }));\n\n accountCmd\n .command(\"summary\")\n .description(\"Get sub-account summary\")\n .option(\"--sub-account-id <id>\", \"sub-account ID\")\n .action(wrapAction(true, async (ctx, opts) => {\n const subAccountId = resolveSubAccountId(opts, ctx.config);\n const result = await getSubAccountSummary(ctx.client, subAccountId);\n output(result, ctx.outputOptions);\n }));\n\n accountCmd\n .command(\"aggregated\")\n .description(\"Get aggregated account summary across all sub-accounts\")\n .action(wrapAction(true, async (ctx) => {\n const result = await getAggregatedAccountSummary(ctx.client);\n output(result, ctx.outputOptions);\n }));\n\n accountCmd\n .command(\"history\")\n .description(\"Get hourly snapshots of sub-account state\")\n .option(\"--sub-account-id <id>\", \"sub-account ID\")\n .option(\"--start-time <time>\", \"start time (unix seconds, ms, ns, or ISO)\")\n .option(\"--end-time <time>\", \"end time\")\n .option(\"--limit <n>\", \"max results per page\")\n .option(\"--cursor <cursor>\", \"pagination cursor\")\n .option(\"--all\", \"auto-paginate all results\")\n .action(wrapAction(true, async (ctx, opts) => {\n const subAccountId = resolveSubAccountId(opts, ctx.config);\n\n const results = await paginateCursor({\n fetchPage: async (cursor) => {\n const body: Record<string, unknown> = {\n sub_account_id: subAccountId,\n ...(opts[\"startTime\"] ? { start_time: parseTimestamp(opts[\"startTime\"] as string) } : {}),\n ...(opts[\"endTime\"] ? { end_time: parseTimestamp(opts[\"endTime\"] as string) } : {}),\n ...(opts[\"limit\"] ? { limit: Number(opts[\"limit\"]) } : {}),\n ...(cursor ? { cursor } : {}),\n };\n const resp = await getAccountHistory(ctx.client, body as Parameters<typeof getAccountHistory>[1]);\n return { result: resp.result ?? [], next: resp.next };\n },\n cursor: opts[\"cursor\"] as string | undefined,\n all: Boolean(opts[\"all\"]),\n outputOptions: ctx.outputOptions,\n });\n\n if (ctx.outputOptions.output !== \"ndjson\" || !opts[\"all\"]) {\n output(results, ctx.outputOptions);\n }\n }));\n};\n","import { Command } from \"commander\";\nimport { ENDPOINTS } from \"../../core/client/endpoints.js\";\nimport { wrapAction, output } from \"../../core/command-helpers.js\";\nimport { paginateCursor } from \"../../core/pagination/cursor.js\";\nimport { parseTimestamp } from \"../../core/safety/validate.js\";\nimport type { HistoryRequest, ApiResponse } from \"../../core/client/types.js\";\nimport type { HttpClient } from \"../../core/client/http.js\";\n\nexport const createDeposit = async (\n _client: HttpClient,\n _params: unknown,\n): Promise<unknown> => {\n throw new Error(\"Deposit creation is not available via REST API. Use the GRVT web interface.\");\n};\n\nexport const getDepositHistory = async (\n client: HttpClient,\n params: HistoryRequest,\n): Promise<ApiResponse<unknown[]>> => {\n return client.post<ApiResponse<unknown[]>>(\"trading\", ENDPOINTS.funds.depositHistory, params);\n};\n\nexport const registerDepositCommands = (parent: Command) => {\n const depositCmd = parent.command(\"deposit\").description(\"Deposit management\");\n\n depositCmd\n .command(\"history\")\n .description(\"Get deposit history\")\n .option(\"--start-time <time>\", \"start time\")\n .option(\"--end-time <time>\", \"end time\")\n .option(\"--limit <n>\", \"max results\")\n .option(\"--cursor <cursor>\", \"pagination cursor\")\n .option(\"--all\", \"auto-paginate all results\")\n .action(wrapAction(true, async (ctx, opts) => {\n const results = await paginateCursor({\n fetchPage: async (cursor) => {\n const body: HistoryRequest = {\n ...(opts[\"startTime\"] ? { start_time: parseTimestamp(opts[\"startTime\"] as string) } : {}),\n ...(opts[\"endTime\"] ? { end_time: parseTimestamp(opts[\"endTime\"] as string) } : {}),\n ...(opts[\"limit\"] ? { limit: Number(opts[\"limit\"]) } : {}),\n ...(cursor ? { cursor } : {}),\n };\n const resp = await getDepositHistory(ctx.client, body);\n return { result: resp.result ?? [], next: resp.next };\n },\n cursor: opts[\"cursor\"] as string | undefined,\n all: Boolean(opts[\"all\"]),\n outputOptions: ctx.outputOptions,\n });\n\n if (ctx.outputOptions.output !== \"ndjson\" || !opts[\"all\"]) {\n output(results, ctx.outputOptions);\n }\n }));\n};\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { createHttpClient } from \"../client/http.js\";\nimport { ENDPOINTS, type GrvtEnvironment } from \"../client/endpoints.js\";\n\nexport interface CurrencyDetail {\n id: number;\n symbol: string;\n balance_decimals: number;\n quantity_multiplier: string;\n}\n\ninterface CurrencyCache {\n env: string;\n fetched_at: number;\n currencies: Record<string, CurrencyDetail>;\n}\n\nconst CACHE_TTL_MS = 60 * 60 * 1000;\n\nconst getCacheDir = (): string => {\n const xdgCache = process.env[\"XDG_CACHE_HOME\"];\n const base = xdgCache || join(homedir(), \".cache\");\n return join(base, \"grvt\");\n};\n\nconst getCachePath = (env: GrvtEnvironment): string =>\n join(getCacheDir(), `currencies-${env}.json`);\n\nconst loadCache = (env: GrvtEnvironment): CurrencyCache | null => {\n const path = getCachePath(env);\n if (!existsSync(path)) return null;\n\n try {\n const raw = readFileSync(path, \"utf-8\");\n const cache = JSON.parse(raw) as CurrencyCache;\n if (cache.env !== env || Date.now() - cache.fetched_at > CACHE_TTL_MS) {\n return null;\n }\n return cache;\n } catch {\n return null;\n }\n};\n\nconst saveCache = (env: GrvtEnvironment, currencies: Record<string, CurrencyDetail>): void => {\n const dir = getCacheDir();\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n const cache: CurrencyCache = { env, fetched_at: Date.now(), currencies };\n writeFileSync(getCachePath(env), JSON.stringify(cache), \"utf-8\");\n};\n\nexport const getCurrencies = async (env: GrvtEnvironment, cookie?: string, accountId?: string): Promise<Record<string, CurrencyDetail>> => {\n const cached = loadCache(env);\n if (cached) return cached.currencies;\n\n const client = createHttpClient({ env, cookie, accountId });\n const response = await client.post<{ result: CurrencyDetail[] }>(\n \"marketData\",\n ENDPOINTS.marketData.currency,\n {},\n );\n\n const currencies: Record<string, CurrencyDetail> = {};\n for (const cur of response.result ?? []) {\n currencies[cur.symbol] = cur;\n }\n\n saveCache(env, currencies);\n return currencies;\n};\n\nexport const getCurrencyId = async (\n env: GrvtEnvironment,\n symbol: string,\n cookie?: string,\n accountId?: string,\n): Promise<number> => {\n const currencies = await getCurrencies(env, cookie, accountId);\n const cur = currencies[symbol.toUpperCase()];\n if (!cur) {\n throw new Error(`Currency not found: ${symbol}. Run 'grvt market currency' to see available currencies.`);\n }\n return cur.id;\n};\n\nexport const getCurrencyDecimals = async (\n env: GrvtEnvironment,\n symbol: string,\n cookie?: string,\n accountId?: string,\n): Promise<number> => {\n const currencies = await getCurrencies(env, cookie, accountId);\n const cur = currencies[symbol.toUpperCase()];\n if (!cur) {\n throw new Error(`Currency not found: ${symbol}. Run 'grvt market currency' to see available currencies.`);\n }\n return cur.balance_decimals;\n};\n","import { Command } from \"commander\";\nimport { ENDPOINTS } from \"../../core/client/endpoints.js\";\nimport { wrapAction, output } from \"../../core/command-helpers.js\";\nimport { paginateCursor } from \"../../core/pagination/cursor.js\";\nimport { parseJsonInput, parseTimestamp, requireOption } from \"../../core/safety/validate.js\";\nimport { logInfo, printOutput } from \"../../core/output/format.js\";\nimport { confirm } from \"../../core/safety/confirm.js\";\nimport { exitAuth } from \"../../core/output/errors.js\";\nimport { buildTransferTypedData, generateNonce, generateExpiration } from \"../../core/signing/eip712.js\";\nimport { signTypedData, createSigner } from \"../../core/signing/signer.js\";\nimport { getCurrencyId, getCurrencyDecimals } from \"../../core/currencies/cache.js\";\nimport type { TransferRequest, HistoryRequest, ApiResponse } from \"../../core/client/types.js\";\nimport type { HttpClient } from \"../../core/client/http.js\";\n\nexport const createTransfer = async (\n client: HttpClient,\n params: TransferRequest,\n): Promise<unknown> => {\n const result = await client.post<ApiResponse>(\"trading\", ENDPOINTS.funds.transfer, params);\n return result.result;\n};\n\nexport const getTransferHistory = async (\n client: HttpClient,\n params: HistoryRequest,\n): Promise<ApiResponse<unknown[]>> => {\n return client.post<ApiResponse<unknown[]>>(\"trading\", ENDPOINTS.funds.transferHistory, params);\n};\n\nexport const registerTransferCommands = (parent: Command) => {\n const transferCmd = parent.command(\"transfer\").description(\"Transfer management\");\n\n transferCmd\n .command(\"create\")\n .description(\"Transfer between sub-accounts\")\n .requiredOption(\"--from-sub-account-id <id>\", \"source sub-account ID\")\n .requiredOption(\"--to-sub-account-id <id>\", \"destination sub-account ID\")\n .requiredOption(\"--currency <symbol>\", \"currency to transfer (e.g. USDT)\")\n .requiredOption(\"--amount <amount>\", \"amount to transfer\")\n .option(\"--json <path>\", \"read full request body from file (@path) or stdin (-)\")\n .option(\"--dry-run\", \"validate and show payload without sending\")\n .action(wrapAction(true, async (ctx, opts) => {\n let body: Record<string, unknown>;\n\n if (opts[\"json\"]) {\n body = parseJsonInput(opts[\"json\"] as string) as Record<string, unknown>;\n } else {\n if (!ctx.config.privateKey) {\n exitAuth(\"Private key required for transfer signing. Run `grvt config set privateKey <key>`.\");\n }\n\n const fromSubAccountId = requireOption(opts[\"fromSubAccountId\"], \"from-sub-account-id\");\n const toSubAccountId = requireOption(opts[\"toSubAccountId\"], \"to-sub-account-id\");\n const currency = (opts[\"currency\"] as string).toUpperCase();\n const amount = opts[\"amount\"] as string;\n const fromAccount = ctx.config.accountId!;\n const toAccount = fromAccount;\n\n const signerAccount = createSigner(ctx.config.privateKey!);\n const tokenCurrency = await getCurrencyId(ctx.config.env, currency, ctx.config.cookie, ctx.config.accountId);\n const balanceDecimals = await getCurrencyDecimals(ctx.config.env, currency, ctx.config.cookie, ctx.config.accountId);\n\n const nonce = generateNonce();\n const expiration = generateExpiration(3600);\n\n const typedData = buildTransferTypedData({\n fromAccount: signerAccount.address,\n fromSubAccount: fromSubAccountId,\n toAccount: signerAccount.address,\n toSubAccount: toSubAccountId,\n tokenCurrency,\n numTokens: amount,\n balanceDecimals,\n expiration,\n nonce,\n }, ctx.config.env);\n\n const sigComponents = await signTypedData(ctx.config.privateKey!, typedData);\n\n body = {\n from_account_id: fromAccount,\n from_sub_account_id: fromSubAccountId,\n to_account_id: toAccount,\n to_sub_account_id: toSubAccountId,\n currency,\n num_tokens: amount,\n signature: {\n signer: sigComponents.signer,\n r: sigComponents.r,\n s: sigComponents.s,\n v: sigComponents.v,\n expiration,\n nonce,\n },\n };\n }\n\n if (opts[\"dryRun\"]) {\n logInfo(\"Dry run - payload that would be sent:\", Boolean(ctx.globalOpts[\"silent\"]));\n printOutput(body, { ...ctx.outputOptions, output: \"json\", pretty: true });\n return;\n }\n\n const skipConfirm = Boolean(ctx.globalOpts[\"yes\"]);\n const ok = await confirm(\n `Transfer ${body[\"num_tokens\"]} ${body[\"currency\"]} from ${body[\"from_sub_account_id\"]} to ${body[\"to_sub_account_id\"]}?`,\n skipConfirm,\n );\n if (!ok) {\n logInfo(\"Aborted.\", Boolean(ctx.globalOpts[\"silent\"]));\n return;\n }\n\n const result = await ctx.client.post<ApiResponse>(\"trading\", ENDPOINTS.funds.transfer, body);\n output(result.result, ctx.outputOptions);\n }));\n\n transferCmd\n .command(\"history\")\n .description(\"Get transfer history\")\n .option(\"--start-time <time>\", \"start time\")\n .option(\"--end-time <time>\", \"end time\")\n .option(\"--limit <n>\", \"max results\")\n .option(\"--cursor <cursor>\", \"pagination cursor\")\n .option(\"--all\", \"auto-paginate all results\")\n .action(wrapAction(true, async (ctx, opts) => {\n const results = await paginateCursor({\n fetchPage: async (cursor) => {\n const body: HistoryRequest = {\n ...(opts[\"startTime\"] ? { start_time: parseTimestamp(opts[\"startTime\"] as string) } : {}),\n ...(opts[\"endTime\"] ? { end_time: parseTimestamp(opts[\"endTime\"] as string) } : {}),\n ...(opts[\"limit\"] ? { limit: Number(opts[\"limit\"]) } : {}),\n ...(cursor ? { cursor } : {}),\n };\n const resp = await getTransferHistory(ctx.client, body);\n return { result: resp.result ?? [], next: resp.next };\n },\n cursor: opts[\"cursor\"] as string | undefined,\n all: Boolean(opts[\"all\"]),\n outputOptions: ctx.outputOptions,\n });\n\n if (ctx.outputOptions.output !== \"ndjson\" || !opts[\"all\"]) {\n output(results, ctx.outputOptions);\n }\n }));\n};\n","import { Command } from \"commander\";\nimport { ENDPOINTS } from \"../../core/client/endpoints.js\";\nimport { wrapAction, output } from \"../../core/command-helpers.js\";\nimport { paginateCursor } from \"../../core/pagination/cursor.js\";\nimport { parseJsonInput, parseTimestamp, requireOption } from \"../../core/safety/validate.js\";\nimport { logInfo, printOutput } from \"../../core/output/format.js\";\nimport { confirm } from \"../../core/safety/confirm.js\";\nimport { exitUsage, exitAuth } from \"../../core/output/errors.js\";\nimport { buildWithdrawalTypedData, generateNonce, generateExpiration } from \"../../core/signing/eip712.js\";\nimport { signTypedData, createSigner } from \"../../core/signing/signer.js\";\nimport { getCurrencyId, getCurrencyDecimals } from \"../../core/currencies/cache.js\";\nimport type { WithdrawalRequest, HistoryRequest, ApiResponse } from \"../../core/client/types.js\";\nimport type { HttpClient } from \"../../core/client/http.js\";\n\nexport const createWithdrawal = async (\n client: HttpClient,\n params: WithdrawalRequest,\n): Promise<unknown> => {\n const result = await client.post<ApiResponse>(\"trading\", ENDPOINTS.funds.withdrawal, params);\n return result.result;\n};\n\nexport const getWithdrawalHistory = async (\n client: HttpClient,\n params: HistoryRequest,\n): Promise<ApiResponse<unknown[]>> => {\n return client.post<ApiResponse<unknown[]>>(\"trading\", ENDPOINTS.funds.withdrawalHistory, params);\n};\n\nexport const registerWithdrawalCommands = (parent: Command) => {\n const withdrawCmd = parent.command(\"withdraw\").description(\"Withdrawal management\");\n\n withdrawCmd\n .command(\"create\")\n .description(\"Withdraw from sub-account to Ethereum address\")\n .option(\"--sub-account-id <id>\", \"source sub-account ID\")\n .requiredOption(\"--to-address <address>\", \"destination Ethereum address\")\n .requiredOption(\"--currency <symbol>\", \"currency to withdraw (e.g. USDT)\")\n .requiredOption(\"--amount <amount>\", \"amount to withdraw\")\n .option(\"--main-account-id <id>\", \"main account ID (defaults to current account)\")\n .option(\"--json <path>\", \"read full request body from file (@path) or stdin (-)\")\n .option(\"--dry-run\", \"validate and show payload without sending\")\n .action(wrapAction(true, async (ctx, opts) => {\n let body: Record<string, unknown>;\n\n if (opts[\"json\"]) {\n body = parseJsonInput(opts[\"json\"] as string) as Record<string, unknown>;\n } else {\n if (!ctx.config.privateKey) {\n exitAuth(\"Private key required for withdrawal signing. Run `grvt config set privateKey <key>`.\");\n }\n\n const toAddress = opts[\"toAddress\"] as string;\n if (!toAddress.startsWith(\"0x\") || toAddress.length !== 42) {\n exitUsage(\"--to-address must be a valid Ethereum address (0x...)\");\n }\n\n const subAccountId = opts[\"subAccountId\"] as string ?? ctx.config.subAccountId;\n if (!subAccountId) {\n exitUsage(\"--sub-account-id is required (or set subAccountId in config)\");\n }\n\n const mainAccountId = (opts[\"mainAccountId\"] as string) ?? requireOption(ctx.config.accountId, \"main-account-id\");\n const currency = (opts[\"currency\"] as string).toUpperCase();\n const amount = opts[\"amount\"] as string;\n\n const signerAccount = createSigner(ctx.config.privateKey!);\n const tokenCurrency = await getCurrencyId(ctx.config.env, currency, ctx.config.cookie, ctx.config.accountId);\n const balanceDecimals = await getCurrencyDecimals(ctx.config.env, currency, ctx.config.cookie, ctx.config.accountId);\n\n const nonce = generateNonce();\n const expiration = generateExpiration(3600);\n\n const typedData = buildWithdrawalTypedData({\n fromAccount: signerAccount.address,\n toEthAddress: toAddress,\n tokenCurrency,\n numTokens: amount,\n balanceDecimals,\n expiration,\n nonce,\n }, ctx.config.env);\n\n const sigComponents = await signTypedData(ctx.config.privateKey!, typedData);\n\n body = {\n main_account_id: mainAccountId,\n from_sub_account_id: subAccountId,\n to_eth_address: toAddress,\n currency,\n num_tokens: amount,\n signature: {\n signer: sigComponents.signer,\n r: sigComponents.r,\n s: sigComponents.s,\n v: sigComponents.v,\n expiration,\n nonce,\n },\n };\n }\n\n if (opts[\"dryRun\"]) {\n logInfo(\"Dry run - payload that would be sent:\", Boolean(ctx.globalOpts[\"silent\"]));\n printOutput(body, { ...ctx.outputOptions, output: \"json\", pretty: true });\n return;\n }\n\n const skipConfirm = Boolean(ctx.globalOpts[\"yes\"]);\n const ok = await confirm(\n `Withdraw ${body[\"num_tokens\"]} ${body[\"currency\"]} to ${body[\"to_eth_address\"]}?`,\n skipConfirm,\n );\n if (!ok) {\n logInfo(\"Aborted.\", Boolean(ctx.globalOpts[\"silent\"]));\n return;\n }\n\n const result = await ctx.client.post<ApiResponse>(\"trading\", ENDPOINTS.funds.withdrawal, body);\n output(result.result, ctx.outputOptions);\n }));\n\n withdrawCmd\n .command(\"history\")\n .description(\"Get withdrawal history\")\n .option(\"--start-time <time>\", \"start time\")\n .option(\"--end-time <time>\", \"end time\")\n .option(\"--limit <n>\", \"max results\")\n .option(\"--cursor <cursor>\", \"pagination cursor\")\n .option(\"--all\", \"auto-paginate all results\")\n .action(wrapAction(true, async (ctx, opts) => {\n const results = await paginateCursor({\n fetchPage: async (cursor) => {\n const body: HistoryRequest = {\n ...(opts[\"startTime\"] ? { start_time: parseTimestamp(opts[\"startTime\"] as string) } : {}),\n ...(opts[\"endTime\"] ? { end_time: parseTimestamp(opts[\"endTime\"] as string) } : {}),\n ...(opts[\"limit\"] ? { limit: Number(opts[\"limit\"]) } : {}),\n ...(cursor ? { cursor } : {}),\n };\n const resp = await getWithdrawalHistory(ctx.client, body);\n return { result: resp.result ?? [], next: resp.next };\n },\n cursor: opts[\"cursor\"] as string | undefined,\n all: Boolean(opts[\"all\"]),\n outputOptions: ctx.outputOptions,\n });\n\n if (ctx.outputOptions.output !== \"ndjson\" || !opts[\"all\"]) {\n output(results, ctx.outputOptions);\n }\n }));\n};\n"],"mappings":"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","names":["c","readFileSync","existsSync","mkdirSync","readFileSync","writeFileSync","join","homedir","existsSync","mkdirSync","readFileSync","writeFileSync","join","homedir","CACHE_TTL_MS","join","homedir","existsSync","readFileSync","mkdirSync","writeFileSync"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_CONFIG,
|
|
4
|
+
SECRET_KEYS,
|
|
5
|
+
configSchema,
|
|
6
|
+
redactConfig
|
|
7
|
+
} from "./chunk-I7C5KKUG.js";
|
|
8
|
+
export {
|
|
9
|
+
DEFAULT_CONFIG,
|
|
10
|
+
SECRET_KEYS,
|
|
11
|
+
configSchema,
|
|
12
|
+
redactConfig
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=schema-LBUHKB7A.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@madeinusmate/grvt-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI tool and library to trade on GRVT markets",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"bin": {
|
|
10
|
+
"grvt": "./dist/cli.js"
|
|
11
|
+
},
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"import": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
},
|
|
18
|
+
"require": {
|
|
19
|
+
"types": "./dist/index.d.cts",
|
|
20
|
+
"default": "./dist/index.cjs"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"LICENSE",
|
|
27
|
+
"README.md"
|
|
28
|
+
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsup",
|
|
31
|
+
"dev": "tsup --watch",
|
|
32
|
+
"test": "vitest run",
|
|
33
|
+
"test:watch": "vitest",
|
|
34
|
+
"test:e2e": "tsx tests/e2e/run.ts",
|
|
35
|
+
"lint": "tsc --noEmit",
|
|
36
|
+
"prepublishOnly": "pnpm build"
|
|
37
|
+
},
|
|
38
|
+
"author": "madeinusmate",
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=20.0.0"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"chalk": "^5.4.1",
|
|
45
|
+
"cli-table3": "^0.6.5",
|
|
46
|
+
"commander": "^13.1.0",
|
|
47
|
+
"smol-toml": "^1.3.1",
|
|
48
|
+
"viem": "^2.23.2",
|
|
49
|
+
"zod": "^3.24.2"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/node": "^25.3.0",
|
|
53
|
+
"tsup": "^8.3.6",
|
|
54
|
+
"tsx": "^4.21.0",
|
|
55
|
+
"typescript": "^5.7.3",
|
|
56
|
+
"vitest": "^3.0.5"
|
|
57
|
+
},
|
|
58
|
+
"packageManager": "pnpm@8.15.9+sha512.499434c9d8fdd1a2794ebf4552b3b25c0a633abcee5bb15e7b5de90f32f47b513aca98cd5cfd001c31f0db454bc3804edccd578501e4ca293a6816166bbd9f81"
|
|
59
|
+
}
|