@earnforge/sdk 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/dist/cjs/index.cjs +1350 -0
- package/dist/cjs/index.d.cts +2929 -0
- package/dist/cjs/index.d.cts.map +1 -0
- package/dist/esm/index.cjs +1350 -0
- package/dist/esm/index.d.cts +2929 -0
- package/dist/esm/index.d.cts.map +1 -0
- package/dist/esm/index.d.mts +2929 -0
- package/dist/esm/index.d.mts.map +1 -0
- package/dist/esm/index.mjs +1298 -0
- package/dist/esm/index.mjs.map +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["DEFAULT_BASE_URL"],"sources":["../../src/schemas/vault.ts","../../src/schemas/chain.ts","../../src/schemas/protocol.ts","../../src/schemas/portfolio.ts","../../src/schemas/quote.ts","../../src/errors.ts","../../src/rate-limiter.ts","../../src/cache.ts","../../src/retry.ts","../../src/clients/earn-data-client.ts","../../src/clients/composer-client.ts","../../src/build-deposit-quote.ts","../../src/build-redeem-quote.ts","../../src/preflight.ts","../../src/risk-scorer.ts","../../src/strategies.ts","../../src/suggest.ts","../../src/gas-optimizer.ts","../../src/watch.ts","../../src/apy-history.ts","../../src/allowance.ts","../../src/index.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { z } from 'zod';\n\n/** Protocol info — always has name + url */\nexport const ProtocolSchema = z.object({\n name: z.string(),\n url: z.string(),\n});\n\n/** Underlying token — symbol, address, decimals */\nexport const UnderlyingTokenSchema = z.object({\n symbol: z.string(),\n address: z.string(),\n decimals: z.number(),\n});\n\n/** Pack (deposit or redeem method) */\nexport const PackSchema = z.object({\n name: z.string(),\n stepsType: z.string(),\n});\n\n/**\n * APY — base and total always present.\n * reward: Morpho returns 0, Euler/Aave return null. Normalize to 0.\n * (Pitfall #17)\n */\nexport const ApySchema = z.object({\n base: z.number(),\n total: z.number(),\n reward: z.number().nullable().transform((v) => v ?? 0),\n});\n\n/**\n * TVL — usd is always a string from the API.\n * We parse it into { raw, parsed, bigint } at the boundary.\n * (Pitfall #8)\n */\nexport const TvlSchema = z.object({\n usd: z.string(),\n});\n\nexport type TvlParsed = {\n raw: string;\n parsed: number;\n bigint: bigint;\n};\n\nexport function parseTvl(tvl: z.infer<typeof TvlSchema>): TvlParsed {\n // Parse bigint directly from string to avoid Number() precision loss for large values\n const integerPart = tvl.usd.split('.')[0] ?? '0';\n return {\n raw: tvl.usd,\n parsed: Number(tvl.usd),\n bigint: BigInt(integerPart),\n };\n}\n\n/**\n * Analytics — apy1d, apy7d, apy30d can all be null.\n * Fallback chain: apy.total → apy30d → apy7d → apy1d\n * (Pitfalls #7, #18)\n */\nexport const AnalyticsSchema = z.object({\n apy: ApySchema,\n tvl: TvlSchema,\n apy1d: z.number().nullable(),\n apy7d: z.number().nullable(),\n apy30d: z.number().nullable(),\n updatedAt: z.string(),\n});\n\n/**\n * Get best available APY with fallback chain.\n * Order: apy.total → apy30d → apy7d → apy1d\n */\nexport function getBestApy(analytics: z.infer<typeof AnalyticsSchema>): number {\n // apy.total is always a number (never null after Zod). If it's non-zero, use it.\n // If it's exactly 0, fall through to historical data as a display hint.\n if (analytics.apy.total !== 0) return analytics.apy.total;\n // Fallback chain for null historical values\n return analytics.apy30d ?? analytics.apy7d ?? analytics.apy1d ?? 0;\n}\n\n/**\n * Vault schema — generated from real fixture (earn.li.fi, Apr 11 2026).\n *\n * Key edge cases handled:\n * - description is optional (~14% of vaults have it) (Pitfall #16)\n * - underlyingTokens can be empty array (Pitfall #15)\n * - apy.reward null → 0 (Pitfall #17)\n * - apy1d/apy7d/apy30d nullable (Pitfall #18)\n * - tvl.usd is a string (Pitfall #8)\n */\nexport const VaultSchema = z.object({\n address: z.string(),\n chainId: z.number(),\n name: z.string(),\n slug: z.string(),\n network: z.string(),\n description: z.string().optional(),\n protocol: ProtocolSchema,\n provider: z.string(),\n syncedAt: z.string(),\n tags: z.array(z.string()),\n underlyingTokens: z.array(UnderlyingTokenSchema),\n lpTokens: z.array(z.unknown()),\n analytics: AnalyticsSchema,\n isTransactional: z.boolean(),\n isRedeemable: z.boolean(),\n depositPacks: z.array(PackSchema),\n redeemPacks: z.array(PackSchema),\n});\n\nexport type Vault = z.infer<typeof VaultSchema>;\n\n/** Paginated vault list response */\nexport const VaultListResponseSchema = z.object({\n data: z.array(VaultSchema),\n nextCursor: z.string().nullable().optional(),\n total: z.number(),\n});\n\nexport type VaultListResponse = z.infer<typeof VaultListResponseSchema>;\n","// SPDX-License-Identifier: Apache-2.0\nimport { z } from 'zod';\n\n/** Chain schema — from GET /v1/earn/chains */\nexport const ChainSchema = z.object({\n chainId: z.number(),\n name: z.string(),\n networkCaip: z.string(),\n});\n\nexport type Chain = z.infer<typeof ChainSchema>;\n\nexport const ChainListResponseSchema = z.array(ChainSchema);\n","// SPDX-License-Identifier: Apache-2.0\nimport { z } from 'zod';\n\n/** Protocol schema — from GET /v1/earn/protocols */\nexport const ProtocolDetailSchema = z.object({\n name: z.string(),\n url: z.string(),\n});\n\nexport type ProtocolDetail = z.infer<typeof ProtocolDetailSchema>;\n\nexport const ProtocolListResponseSchema = z.array(ProtocolDetailSchema);\n","// SPDX-License-Identifier: Apache-2.0\nimport { z } from 'zod';\n\n/** Position asset */\nexport const PositionAssetSchema = z.object({\n address: z.string(),\n name: z.string(),\n symbol: z.string(),\n decimals: z.number(),\n});\n\n/** Single portfolio position */\nexport const PositionSchema = z.object({\n chainId: z.number(),\n protocolName: z.string(),\n asset: PositionAssetSchema,\n balanceUsd: z.string(),\n balanceNative: z.string(),\n});\n\nexport type Position = z.infer<typeof PositionSchema>;\n\n/** Portfolio response */\nexport const PortfolioResponseSchema = z.object({\n positions: z.array(PositionSchema),\n});\n\nexport type PortfolioResponse = z.infer<typeof PortfolioResponseSchema>;\n","// SPDX-License-Identifier: Apache-2.0\nimport { z } from 'zod';\n\n/** Token info in Composer responses */\nexport const ComposerTokenSchema = z.object({\n address: z.string(),\n chainId: z.number(),\n symbol: z.string(),\n decimals: z.number(),\n name: z.string(),\n coinKey: z.string().optional(),\n logoURI: z.string().optional(),\n priceUSD: z.string().optional(),\n tags: z.array(z.string()).optional(),\n verificationStatus: z.string().optional(),\n verificationStatusBreakdown: z.array(z.unknown()).optional(),\n});\n\n/** Tool details */\nexport const ToolDetailsSchema = z.object({\n key: z.string(),\n name: z.string(),\n logoURI: z.string().optional(),\n});\n\n/** Fee split */\nexport const FeeSplitSchema = z.object({\n integratorFee: z.string(),\n lifiFee: z.string(),\n});\n\n/** Fee cost */\nexport const FeeCostSchema = z.object({\n name: z.string(),\n description: z.string().optional(),\n token: ComposerTokenSchema,\n amount: z.string(),\n amountUSD: z.string(),\n percentage: z.string(),\n included: z.boolean(),\n feeSplit: FeeSplitSchema.optional(),\n});\n\n/** Gas cost */\nexport const GasCostSchema = z.object({\n type: z.string(),\n price: z.string().optional(),\n estimate: z.string().optional(),\n limit: z.string().optional(),\n amount: z.string(),\n amountUSD: z.string(),\n token: ComposerTokenSchema,\n});\n\n/** Quote action */\nexport const QuoteActionSchema = z.object({\n fromToken: ComposerTokenSchema,\n fromAmount: z.string(),\n toToken: ComposerTokenSchema,\n fromChainId: z.number(),\n toChainId: z.number(),\n slippage: z.number(),\n fromAddress: z.string(),\n toAddress: z.string(),\n});\n\n/** Quote estimate */\nexport const QuoteEstimateSchema = z.object({\n tool: z.string(),\n approvalAddress: z.string().optional(),\n toAmountMin: z.string(),\n toAmount: z.string(),\n fromAmount: z.string(),\n feeCosts: z.array(FeeCostSchema).optional(),\n gasCosts: z.array(GasCostSchema).optional(),\n executionDuration: z.number(),\n fromAmountUSD: z.string().optional(),\n toAmountUSD: z.string().optional(),\n});\n\n/** Transaction request — ready to sign */\nexport const TransactionRequestSchema = z.object({\n to: z.string(),\n data: z.string(),\n value: z.string(),\n chainId: z.number(),\n gasPrice: z.string().optional(),\n gasLimit: z.string().optional(),\n from: z.string().optional(),\n});\n\nexport type TransactionRequest = z.infer<typeof TransactionRequestSchema>;\n\n/** Included step */\nexport const IncludedStepSchema = z.object({\n id: z.string(),\n type: z.string(),\n tool: z.string(),\n toolDetails: ToolDetailsSchema.optional(),\n action: z.record(z.unknown()),\n estimate: z.record(z.unknown()),\n});\n\n/** Full Composer quote response */\nexport const QuoteResponseSchema = z.object({\n type: z.string(),\n id: z.string(),\n tool: z.string(),\n toolDetails: ToolDetailsSchema.optional(),\n action: QuoteActionSchema,\n estimate: QuoteEstimateSchema,\n includedSteps: z.array(IncludedStepSchema).optional(),\n integrator: z.string().optional(),\n transactionRequest: TransactionRequestSchema,\n transactionId: z.string().optional(),\n});\n\nexport type QuoteResponse = z.infer<typeof QuoteResponseSchema>;\n","// SPDX-License-Identifier: Apache-2.0\n\nexport class EarnForgeError extends Error {\n constructor(message: string, public readonly code: string) {\n super(message);\n this.name = 'EarnForgeError';\n }\n}\n\nexport class EarnApiError extends EarnForgeError {\n constructor(\n message: string,\n public readonly status: number,\n public readonly url: string,\n ) {\n super(message, 'EARN_API_ERROR');\n this.name = 'EarnApiError';\n }\n}\n\nexport class ComposerError extends EarnForgeError {\n constructor(\n message: string,\n public readonly status: number,\n ) {\n super(message, 'COMPOSER_ERROR');\n this.name = 'ComposerError';\n }\n}\n\nexport class PreflightError extends EarnForgeError {\n constructor(\n message: string,\n public readonly issues: PreflightIssue[],\n ) {\n super(message, 'PREFLIGHT_ERROR');\n this.name = 'PreflightError';\n }\n}\n\nexport class RateLimitError extends EarnForgeError {\n constructor(\n public readonly retryAfter: number,\n ) {\n super(`Rate limited. Retry after ${retryAfter}ms`, 'RATE_LIMIT');\n this.name = 'RateLimitError';\n }\n}\n\nexport interface PreflightIssue {\n code: string;\n message: string;\n severity: 'error' | 'warning';\n}\n","// SPDX-License-Identifier: Apache-2.0\nimport { RateLimitError } from './errors.js';\n\n/**\n * Token-bucket rate limiter with queued async acquisition.\n * Default: 100 requests per minute for Earn Data API (Pitfall #14).\n */\nexport class TokenBucketRateLimiter {\n private tokens: number;\n private lastRefill: number;\n private readonly maxTokens: number;\n private readonly refillRate: number; // tokens per ms\n private _queue: Promise<void> = Promise.resolve();\n\n constructor(maxRequestsPerMinute = 100) {\n this.maxTokens = maxRequestsPerMinute;\n this.tokens = maxRequestsPerMinute;\n this.refillRate = maxRequestsPerMinute / 60_000;\n this.lastRefill = Date.now();\n }\n\n private refill(): void {\n const now = Date.now();\n const elapsed = now - this.lastRefill;\n this.tokens = Math.min(this.maxTokens, this.tokens + elapsed * this.refillRate);\n this.lastRefill = now;\n }\n\n acquire(): void {\n this.refill();\n if (this.tokens < 1) {\n const waitMs = Math.ceil((1 - this.tokens) / this.refillRate);\n throw new RateLimitError(waitMs);\n }\n this.tokens -= 1;\n }\n\n /**\n * Queued async acquire — serializes concurrent callers to prevent\n * race conditions where multiple callers pass the token check simultaneously.\n */\n async acquireAsync(): Promise<void> {\n this._queue = this._queue.then(() => this._acquireInternal());\n return this._queue;\n }\n\n private async _acquireInternal(): Promise<void> {\n this.refill();\n if (this.tokens < 1) {\n const waitMs = Math.ceil((1 - this.tokens) / this.refillRate);\n await new Promise((resolve) => setTimeout(resolve, waitMs));\n this.refill();\n }\n this.tokens -= 1;\n }\n\n get remaining(): number {\n this.refill();\n return Math.floor(this.tokens);\n }\n}\n","// SPDX-License-Identifier: Apache-2.0\n\ninterface CacheEntry<T> {\n value: T;\n expiresAt: number;\n}\n\n/**\n * In-memory LRU cache with configurable TTL.\n * Evicts least-recently-used entries when capacity is reached.\n */\nexport class LRUCache<T> {\n private readonly map = new Map<string, CacheEntry<T>>();\n private readonly maxSize: number;\n private readonly ttl: number;\n\n constructor(options: { maxSize?: number; ttl?: number } = {}) {\n this.maxSize = options.maxSize ?? 256;\n this.ttl = options.ttl ?? 60_000;\n }\n\n get(key: string): T | undefined {\n const entry = this.map.get(key);\n if (!entry) return undefined;\n if (Date.now() > entry.expiresAt) {\n this.map.delete(key);\n return undefined;\n }\n // Move to end (most recently used)\n this.map.delete(key);\n this.map.set(key, entry);\n return entry.value;\n }\n\n set(key: string, value: T): void {\n // Delete first to update insertion order\n this.map.delete(key);\n if (this.map.size >= this.maxSize) {\n // Evict oldest\n const firstKey = this.map.keys().next().value;\n if (firstKey !== undefined) {\n this.map.delete(firstKey);\n }\n }\n this.map.set(key, { value, expiresAt: Date.now() + this.ttl });\n }\n\n has(key: string): boolean {\n return this.get(key) !== undefined;\n }\n\n clear(): void {\n this.map.clear();\n }\n\n get size(): number {\n return this.map.size;\n }\n}\n","// SPDX-License-Identifier: Apache-2.0\nimport { EarnApiError, ComposerError } from './errors.js';\n\nexport interface RetryOptions {\n maxRetries?: number;\n baseDelay?: number;\n maxDelay?: number;\n}\n\nconst DEFAULTS: Required<RetryOptions> = {\n maxRetries: 3,\n baseDelay: 1000,\n maxDelay: 10_000,\n};\n\n/**\n * Retry with exponential backoff on 429 / 5xx / network errors.\n */\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n options?: RetryOptions,\n): Promise<T> {\n const opts = { ...DEFAULTS, ...options };\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {\n try {\n return await fn();\n } catch (error: unknown) {\n lastError = error;\n if (attempt === opts.maxRetries) break;\n if (!isRetryable(error)) throw error;\n\n const delay = Math.min(\n opts.baseDelay * 2 ** attempt + Math.random() * 200,\n opts.maxDelay,\n );\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n\n throw lastError;\n}\n\nexport function isRetryable(error: unknown): boolean {\n // Match our typed error classes directly\n if (error instanceof EarnApiError) {\n return error.status === 429 || error.status >= 500;\n }\n if (error instanceof ComposerError) {\n return error.status === 429 || error.status >= 500;\n }\n if (error instanceof Error) {\n const msg = error.message.toLowerCase();\n if (msg.includes('rate limit') || msg.includes('429')) return true;\n if (msg.includes('network') || msg.includes('fetch') || msg.includes('econnreset')) {\n return true;\n }\n }\n return false;\n}\n","// SPDX-License-Identifier: Apache-2.0\nimport { EarnApiError } from '../errors.js';\nimport { TokenBucketRateLimiter } from '../rate-limiter.js';\nimport { LRUCache } from '../cache.js';\nimport { withRetry, type RetryOptions } from '../retry.js';\nimport {\n VaultListResponseSchema,\n VaultSchema,\n ChainListResponseSchema,\n ProtocolListResponseSchema,\n PortfolioResponseSchema,\n type Vault,\n type VaultListResponse,\n type Chain,\n type ProtocolDetail,\n type PortfolioResponse,\n} from '../schemas/index.js';\n\n/**\n * Earn Data API base URL — earn.li.fi (Pitfall #1).\n * No auth required (Pitfall #2).\n */\nconst DEFAULT_BASE_URL = 'https://earn.li.fi';\n\nexport interface EarnDataClientOptions {\n baseUrl?: string;\n cache?: { ttl?: number; maxSize?: number };\n rateLimiter?: { maxPerMinute?: number };\n retry?: RetryOptions;\n}\n\nexport interface VaultListParams {\n chainId?: number;\n asset?: string;\n minTvl?: number;\n sortBy?: string;\n cursor?: string;\n}\n\nexport class EarnDataClient {\n private readonly baseUrl: string;\n private readonly cache: LRUCache<unknown>;\n private readonly rateLimiter: TokenBucketRateLimiter;\n private readonly retryOpts: RetryOptions;\n\n constructor(options: EarnDataClientOptions = {}) {\n this.baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;\n this.cache = new LRUCache(options.cache);\n this.rateLimiter = new TokenBucketRateLimiter(options.rateLimiter?.maxPerMinute ?? 100);\n this.retryOpts = options.retry ?? {};\n }\n\n /**\n * Low-level fetch with rate limiting, caching, retry.\n * No auth header — Earn Data API is public (Pitfall #2).\n */\n private async fetch<T>(path: string, cacheKey: string, parse: (data: unknown) => T): Promise<T> {\n const cached = this.cache.get(cacheKey) as T | undefined;\n if (cached !== undefined) return cached;\n\n await this.rateLimiter.acquireAsync();\n\n const result = await withRetry(async () => {\n const url = `${this.baseUrl}${path}`;\n const res = await globalThis.fetch(url);\n if (!res.ok) {\n const body = await res.text().catch(() => '');\n throw new EarnApiError(\n `Earn API error: ${res.status} ${res.statusText}. ${body}`.trim(),\n res.status,\n url,\n );\n }\n const json = await res.json();\n return parse(json);\n }, this.retryOpts);\n\n this.cache.set(cacheKey, result);\n return result;\n }\n\n /** Fetch a single page of vaults */\n async listVaults(params: VaultListParams = {}): Promise<VaultListResponse> {\n const searchParams = new URLSearchParams();\n if (params.chainId !== undefined) searchParams.set('chainId', String(params.chainId));\n if (params.asset) searchParams.set('asset', params.asset);\n if (params.minTvl !== undefined) searchParams.set('minTvl', String(params.minTvl));\n if (params.sortBy) searchParams.set('sortBy', params.sortBy);\n if (params.cursor) searchParams.set('cursor', params.cursor);\n\n const qs = searchParams.toString();\n const path = `/v1/earn/vaults${qs ? `?${qs}` : ''}`;\n return this.fetch(path, `vaults:${qs}`, (data) => VaultListResponseSchema.parse(data));\n }\n\n /**\n * Async iterator for all vaults with auto-pagination via nextCursor.\n * Page size is 50 (API default).\n * (Pitfall #6)\n */\n async *listAllVaults(params: Omit<VaultListParams, 'cursor'> = {}): AsyncIterable<Vault> {\n let cursor: string | undefined;\n do {\n const response = await this.listVaults({ ...params, cursor });\n for (const vault of response.data) {\n yield vault;\n }\n cursor = response.nextCursor ?? undefined;\n } while (cursor);\n }\n\n /**\n * Get a single vault by chainId + address.\n * chainId MUST be a number, not chain name (Pitfall — /vaults/Base/0x... returns 400).\n */\n async getVault(chainId: number, address: string): Promise<Vault> {\n if (!/^0x[0-9a-fA-F]{40}$/.test(address)) {\n throw new EarnApiError(`Invalid vault address format: \"${address}\"`, 400, address);\n }\n const path = `/v1/earn/vaults/${chainId}/${encodeURIComponent(address)}`;\n return this.fetch(path, `vault:${chainId}:${address}`, (data) => VaultSchema.parse(data));\n }\n\n /** Get a vault by slug (e.g., \"8453-0xbeef...\") */\n async getVaultBySlug(slug: string): Promise<Vault> {\n const dashIdx = slug.indexOf('-');\n if (dashIdx === -1) throw new EarnApiError('Invalid slug format', 400, slug);\n const chainId = Number(slug.slice(0, dashIdx));\n const address = slug.slice(dashIdx + 1);\n if (Number.isNaN(chainId)) throw new EarnApiError('Invalid chainId in slug', 400, slug);\n if (!/^0x[0-9a-fA-F]{40}$/.test(address)) {\n throw new EarnApiError(`Invalid address in slug: \"${address}\"`, 400, slug);\n }\n return this.getVault(chainId, address);\n }\n\n /** List supported chains */\n async listChains(): Promise<Chain[]> {\n return this.fetch('/v1/earn/chains', 'chains', (data) => ChainListResponseSchema.parse(data));\n }\n\n /** List supported protocols */\n async listProtocols(): Promise<ProtocolDetail[]> {\n return this.fetch('/v1/earn/protocols', 'protocols', (data) =>\n ProtocolListResponseSchema.parse(data),\n );\n }\n\n /** Get portfolio positions for a wallet */\n async getPortfolio(walletAddress: string): Promise<PortfolioResponse> {\n const path = `/v1/earn/portfolio/${walletAddress}/positions`;\n return this.fetch(path, `portfolio:${walletAddress}`, (data) =>\n PortfolioResponseSchema.parse(data),\n );\n }\n\n /** Rate limiter remaining tokens */\n get rateLimitRemaining(): number {\n return this.rateLimiter.remaining;\n }\n\n /** Clear cache */\n clearCache(): void {\n this.cache.clear();\n }\n}\n","// SPDX-License-Identifier: Apache-2.0\nimport { ComposerError } from '../errors.js';\nimport { withRetry, type RetryOptions } from '../retry.js';\nimport { QuoteResponseSchema, type QuoteResponse } from '../schemas/index.js';\n\n/**\n * Composer base URL — li.quest (Pitfall #1).\n * Requires x-lifi-api-key header (Pitfall #3).\n * Endpoint is GET, not POST (Pitfall #4).\n */\nconst DEFAULT_BASE_URL = 'https://li.quest';\n\nexport interface ComposerClientOptions {\n apiKey: string;\n baseUrl?: string;\n retry?: RetryOptions;\n}\n\nexport interface QuoteParams {\n fromChain: number;\n toChain: number;\n fromToken: string;\n toToken: string;\n fromAddress: string;\n toAddress: string;\n fromAmount: string;\n slippage?: number;\n fromAmountForGas?: string;\n}\n\nexport class ComposerClient {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n private readonly retryOpts: RetryOptions;\n\n constructor(options: ComposerClientOptions) {\n if (!options.apiKey) {\n throw new ComposerError(\n 'Missing Composer API key. Set LIFI_API_KEY environment variable or pass composerApiKey to createEarnForge().',\n 401,\n );\n }\n this.apiKey = options.apiKey;\n this.baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;\n this.retryOpts = options.retry ?? {};\n }\n\n /**\n * Get a deposit/swap/bridge quote.\n * Uses GET, not POST (Pitfall #4).\n * Sends x-lifi-api-key header (Pitfall #3).\n */\n async getQuote(params: QuoteParams): Promise<QuoteResponse> {\n return withRetry(async () => {\n const searchParams = new URLSearchParams({\n fromChain: String(params.fromChain),\n toChain: String(params.toChain),\n fromToken: params.fromToken,\n toToken: params.toToken,\n fromAddress: params.fromAddress,\n toAddress: params.toAddress,\n fromAmount: params.fromAmount,\n });\n\n if (params.slippage !== undefined) {\n searchParams.set('slippage', String(params.slippage));\n }\n if (params.fromAmountForGas) {\n searchParams.set('fromAmountForGas', params.fromAmountForGas);\n }\n\n const url = `${this.baseUrl}/v1/quote?${searchParams.toString()}`;\n\n // GET, not POST (Pitfall #4)\n const res = await globalThis.fetch(url, {\n method: 'GET',\n headers: {\n 'x-lifi-api-key': this.apiKey,\n },\n });\n\n if (!res.ok) {\n const body = await res.text().catch(() => '');\n throw new ComposerError(\n `Composer error: ${res.status} ${res.statusText}. ${body}`,\n res.status,\n );\n }\n\n const json = await res.json();\n return QuoteResponseSchema.parse(json);\n }, this.retryOpts);\n }\n}\n","// SPDX-License-Identifier: Apache-2.0\nimport type { ComposerClient, QuoteParams } from './clients/index.js';\nimport type { Vault, QuoteResponse } from './schemas/index.js';\nimport { EarnForgeError } from './errors.js';\n\nexport interface DepositQuoteOptions {\n fromAmount: string;\n wallet: string;\n fromToken?: string;\n fromChain?: number;\n slippage?: number;\n fromAmountForGas?: string;\n}\n\nexport interface DepositQuoteResult {\n quote: QuoteResponse;\n vault: Vault;\n humanAmount: string;\n rawAmount: string;\n decimals: number;\n}\n\n/**\n * Build a deposit quote with all 18 pitfalls handled:\n *\n * - toToken = vault.address (Pitfall #5)\n * - Uses correct decimals from underlyingTokens (Pitfall #9)\n * - Validates isTransactional (Pitfall #13)\n * - Validates underlyingTokens is non-empty (Pitfall #15)\n * - GET request via ComposerClient (Pitfall #4)\n * - API key in header via ComposerClient (Pitfall #3)\n */\nexport async function buildDepositQuote(\n vault: Vault,\n options: DepositQuoteOptions,\n composer: ComposerClient,\n): Promise<DepositQuoteResult> {\n // Validate wallet address format\n if (!/^0x[0-9a-fA-F]{40}$/.test(options.wallet)) {\n throw new EarnForgeError(\n `Invalid wallet address: \"${options.wallet}\". Must be a 0x-prefixed 40-hex-char address.`,\n 'INVALID_WALLET',\n );\n }\n\n // Pitfall #13: non-transactional vault\n if (!vault.isTransactional) {\n throw new EarnForgeError(\n `Vault ${vault.slug} is not transactional — deposits are not supported.`,\n 'NOT_TRANSACTIONAL',\n );\n }\n\n // Pitfall #15: empty underlyingTokens\n if (vault.underlyingTokens.length === 0 && !options.fromToken) {\n throw new EarnForgeError(\n `Vault ${vault.slug} has no underlyingTokens. You must specify fromToken explicitly.`,\n 'NO_UNDERLYING_TOKENS',\n );\n }\n\n // Determine fromToken: explicit override or first underlying token (Pitfall #5 helper)\n const fromTokenAddr =\n options.fromToken ?? vault.underlyingTokens[0]?.address;\n\n if (!fromTokenAddr) {\n throw new EarnForgeError(\n 'Cannot determine fromToken — vault has no underlyingTokens and none was provided.',\n 'NO_FROM_TOKEN',\n );\n }\n\n // Cross-chain guard: vault's underlyingToken address is on vault's chain, not source chain\n const fromChain = options.fromChain ?? vault.chainId;\n if (fromChain !== vault.chainId && !options.fromToken) {\n throw new EarnForgeError(\n `Cross-chain deposits require an explicit fromToken. The vault's underlyingTokens are on chain ${vault.chainId}, not chain ${fromChain}.`,\n 'CROSS_CHAIN_FROM_TOKEN_REQUIRED',\n );\n }\n\n // Determine decimals for amount conversion (Pitfall #9)\n const decimals =\n vault.underlyingTokens.find(\n (t) => t.address.toLowerCase() === fromTokenAddr.toLowerCase(),\n )?.decimals ?? 18;\n\n // Convert human amount to smallest unit\n const rawAmount = toSmallestUnit(options.fromAmount, decimals);\n\n const quoteParams: QuoteParams = {\n fromChain,\n toChain: vault.chainId,\n fromToken: fromTokenAddr,\n toToken: vault.address, // Pitfall #5: toToken = vault address, NOT underlying\n fromAddress: options.wallet,\n toAddress: options.wallet,\n fromAmount: rawAmount,\n slippage: options.slippage,\n fromAmountForGas: options.fromAmountForGas,\n };\n\n const quote = await composer.getQuote(quoteParams);\n\n return {\n quote,\n vault,\n humanAmount: options.fromAmount,\n rawAmount,\n decimals,\n };\n}\n\n/**\n * Convert a human-readable amount to the smallest unit.\n * e.g., \"1\" with 6 decimals → \"1000000\" (Pitfall #9)\n */\nexport function toSmallestUnit(amount: string, decimals: number): string {\n if (!amount || !/^\\d+(\\.\\d+)?$/.test(amount)) {\n throw new EarnForgeError(\n `Invalid amount \"${amount}\". Must be a positive numeric string (e.g., \"100\" or \"1.5\").`,\n 'INVALID_AMOUNT',\n );\n }\n\n const parts = amount.split('.');\n const whole = parts[0] ?? '0';\n let fractional = parts[1] ?? '';\n\n // Pad or truncate fractional part\n if (fractional.length > decimals) {\n fractional = fractional.slice(0, decimals);\n } else {\n fractional = fractional.padEnd(decimals, '0');\n }\n\n // Remove leading zeros\n const raw = `${whole}${fractional}`.replace(/^0+/, '') || '0';\n return raw;\n}\n\n/**\n * Convert smallest unit to human-readable amount.\n */\nexport function fromSmallestUnit(rawAmount: string, decimals: number): string {\n if (decimals === 0) return rawAmount;\n const padded = rawAmount.padStart(decimals + 1, '0');\n const whole = padded.slice(0, padded.length - decimals);\n const fractional = padded.slice(padded.length - decimals).replace(/0+$/, '');\n return fractional ? `${whole}.${fractional}` : whole;\n}\n","// SPDX-License-Identifier: Apache-2.0\nimport type { ComposerClient, QuoteParams } from './clients/index.js';\nimport type { Vault, QuoteResponse } from './schemas/index.js';\nimport { EarnForgeError } from './errors.js';\nimport { toSmallestUnit } from './build-deposit-quote.js';\n\nexport interface RedeemQuoteOptions {\n /** Amount of vault tokens to redeem (human-readable) */\n fromAmount: string;\n wallet: string;\n /** Token to receive. Defaults to underlying token on vault chain. */\n toToken?: string;\n /** Destination chain. Defaults to vault chain. */\n toChain?: number;\n slippage?: number;\n}\n\nexport interface RedeemQuoteResult {\n quote: QuoteResponse;\n vault: Vault;\n humanAmount: string;\n rawAmount: string;\n}\n\n/**\n * Build a withdrawal/redeem quote.\n *\n * Withdrawal is the reverse of deposit:\n * - fromToken = vault.address (the vault share token)\n * - toToken = underlying token address (what you get back)\n *\n * Uses the same Composer /v1/quote endpoint — just swapped tokens.\n */\nexport async function buildRedeemQuote(\n vault: Vault,\n options: RedeemQuoteOptions,\n composer: ComposerClient,\n): Promise<RedeemQuoteResult> {\n // Validate wallet\n if (!/^0x[0-9a-fA-F]{40}$/.test(options.wallet)) {\n throw new EarnForgeError(\n `Invalid wallet address: \"${options.wallet}\".`,\n 'INVALID_WALLET',\n );\n }\n\n if (!vault.isRedeemable) {\n throw new EarnForgeError(\n `Vault ${vault.slug} is not redeemable — withdrawals are not supported.`,\n 'NOT_REDEEMABLE',\n );\n }\n\n // Vault share tokens typically use 18 decimals (confirmed from Composer quote fixture)\n const vaultDecimals = 18;\n const rawAmount = toSmallestUnit(options.fromAmount, vaultDecimals);\n\n // Determine destination token: explicit or first underlying\n const toToken =\n options.toToken ?? vault.underlyingTokens[0]?.address;\n\n if (!toToken) {\n throw new EarnForgeError(\n 'Cannot determine toToken for redeem — vault has no underlyingTokens and none was provided.',\n 'NO_TO_TOKEN',\n );\n }\n\n const toChain = options.toChain ?? vault.chainId;\n\n // Cross-chain redeem guard\n if (toChain !== vault.chainId && !options.toToken) {\n throw new EarnForgeError(\n `Cross-chain redeems require an explicit toToken address on the destination chain.`,\n 'CROSS_CHAIN_TO_TOKEN_REQUIRED',\n );\n }\n\n const quoteParams: QuoteParams = {\n fromChain: vault.chainId,\n toChain,\n fromToken: vault.address, // Redeem: fromToken = vault share token\n toToken, // Redeem: toToken = underlying (reverse of deposit)\n fromAddress: options.wallet,\n toAddress: options.wallet,\n fromAmount: rawAmount,\n slippage: options.slippage,\n };\n\n const quote = await composer.getQuote(quoteParams);\n\n return {\n quote,\n vault,\n humanAmount: options.fromAmount,\n rawAmount,\n };\n}\n","// SPDX-License-Identifier: Apache-2.0\nimport type { Vault } from './schemas/index.js';\nimport type { PreflightIssue } from './errors.js';\nimport { toSmallestUnit } from './build-deposit-quote.js';\n\nexport interface PreflightReport {\n ok: boolean;\n issues: PreflightIssue[];\n vault: Vault;\n wallet: string;\n}\n\nexport interface PreflightOptions {\n walletChainId?: number;\n nativeBalance?: bigint;\n tokenBalance?: bigint;\n tokenDecimals?: number;\n depositAmount?: string;\n /** If true, cross-chain deposit is intended — skip chain mismatch error */\n crossChain?: boolean;\n}\n\n/**\n * Run preflight checks before a deposit:\n * - isTransactional check (Pitfall #13)\n * - Chain mismatch check (Pitfall #12) — warning for cross-chain, error for same-chain\n * - Gas token balance check (Pitfall #11)\n * - Token balance check (uses string-based toSmallestUnit to avoid float precision loss)\n * - underlyingTokens existence (Pitfall #15)\n * - isRedeemable warning\n */\nexport function preflight(\n vault: Vault,\n wallet: string,\n options: PreflightOptions = {},\n): PreflightReport {\n const issues: PreflightIssue[] = [];\n\n // Pitfall #13\n if (!vault.isTransactional) {\n issues.push({\n code: 'NOT_TRANSACTIONAL',\n message: `Vault ${vault.slug} is not transactional — cannot deposit.`,\n severity: 'error',\n });\n }\n\n // Pitfall #12: chain mismatch — warning for cross-chain (Composer handles bridging)\n if (options.walletChainId !== undefined && options.walletChainId !== vault.chainId) {\n issues.push({\n code: 'CHAIN_MISMATCH',\n message: `Wallet is on chain ${options.walletChainId} but vault is on chain ${vault.chainId}. ${options.crossChain ? 'Composer will handle cross-chain bridging.' : 'Switch network or use cross-chain deposit.'}`,\n severity: 'warning',\n });\n }\n\n // Pitfall #11: no gas token\n if (options.nativeBalance !== undefined && options.nativeBalance === 0n) {\n issues.push({\n code: 'NO_GAS',\n message: 'Wallet has 0 native gas token. Transaction will fail.',\n severity: 'error',\n });\n }\n\n // Pitfall #15: empty underlyingTokens\n if (vault.underlyingTokens.length === 0) {\n issues.push({\n code: 'NO_UNDERLYING_TOKENS',\n message: 'Vault has no underlyingTokens metadata. You must specify fromToken manually.',\n severity: 'warning',\n });\n }\n\n // Token balance check — uses string-based conversion to avoid float precision loss\n if (options.tokenBalance !== undefined && options.depositAmount !== undefined) {\n const decimals = options.tokenDecimals ?? vault.underlyingTokens[0]?.decimals ?? 18;\n const requiredRaw = BigInt(toSmallestUnit(options.depositAmount, decimals));\n if (options.tokenBalance < requiredRaw) {\n issues.push({\n code: 'INSUFFICIENT_BALANCE',\n message: `Insufficient token balance. Have: ${options.tokenBalance}, need: ${requiredRaw}`,\n severity: 'error',\n });\n }\n }\n\n // Redeemability warning\n if (!vault.isRedeemable) {\n issues.push({\n code: 'NOT_REDEEMABLE',\n message: 'Vault is not redeemable — you may not be able to withdraw.',\n severity: 'warning',\n });\n }\n\n return {\n ok: issues.filter((i) => i.severity === 'error').length === 0,\n issues,\n vault,\n wallet,\n };\n}\n","// SPDX-License-Identifier: Apache-2.0\nimport type { Vault } from './schemas/index.js';\nimport { parseTvl } from './schemas/vault.js';\n\nexport interface RiskBreakdown {\n tvl: number;\n apyStability: number;\n protocol: number;\n redeemability: number;\n assetType: number;\n}\n\nexport interface RiskScore {\n score: number;\n breakdown: RiskBreakdown;\n label: 'low' | 'medium' | 'high';\n}\n\n/** Blue-chip / mature protocol tiers */\nconst PROTOCOL_TIERS: Record<string, number> = {\n 'aave-v3': 9,\n 'morpho-v1': 9,\n 'euler-v2': 7,\n 'pendle': 7,\n 'maple': 6,\n 'ethena-usde': 7,\n 'ether.fi-liquid': 7,\n 'ether.fi-stake': 7,\n 'upshift': 5,\n 'neverland': 4,\n 'yo-protocol': 4,\n};\n\n/**\n * Compute a composite 0–10 risk score for a vault.\n *\n * Dimensions:\n * - TVL magnitude: higher TVL = lower risk\n * - APY stability: small divergence between apy1d/apy30d/total = more stable\n * - Protocol maturity: known blue-chip protocols score higher\n * - Redeemability: non-redeemable = liquidity risk\n * - Asset type: stablecoin tag = lower asset risk\n */\nexport function riskScore(vault: Vault): RiskScore {\n const tvlScore = scoreTvl(vault);\n const apyScore = scoreApyStability(vault);\n const protocolScore = scoreProtocol(vault);\n const redeemScore = vault.isRedeemable ? 10 : 3;\n const assetScore = vault.tags.includes('stablecoin') ? 9 : 5;\n\n // Weighted average\n const weights = { tvl: 0.25, apy: 0.2, protocol: 0.25, redeem: 0.15, asset: 0.15 };\n const score =\n tvlScore * weights.tvl +\n apyScore * weights.apy +\n protocolScore * weights.protocol +\n redeemScore * weights.redeem +\n assetScore * weights.asset;\n\n const rounded = Math.round(score * 10) / 10;\n\n return {\n score: rounded,\n breakdown: {\n tvl: tvlScore,\n apyStability: apyScore,\n protocol: protocolScore,\n redeemability: redeemScore,\n assetType: assetScore,\n },\n label: rounded >= 7 ? 'low' : rounded >= 4 ? 'medium' : 'high',\n };\n}\n\nfunction scoreTvl(vault: Vault): number {\n const tvl = parseTvl(vault.analytics.tvl);\n if (tvl.parsed >= 100_000_000) return 10;\n if (tvl.parsed >= 50_000_000) return 9;\n if (tvl.parsed >= 10_000_000) return 8;\n if (tvl.parsed >= 5_000_000) return 7;\n if (tvl.parsed >= 1_000_000) return 5;\n if (tvl.parsed >= 100_000) return 3;\n return 1;\n}\n\nfunction scoreApyStability(vault: Vault): number {\n const { apy, apy1d, apy30d } = vault.analytics;\n const total = apy.total;\n\n // If we don't have historical data, moderate score\n if (apy30d === null && apy1d === null) return 5;\n\n const ref = apy30d ?? apy1d ?? total;\n if (ref === 0 || total === 0) return 5;\n\n const divergence = Math.abs(total - ref) / Math.max(total, ref);\n\n if (divergence < 0.05) return 10;\n if (divergence < 0.1) return 8;\n if (divergence < 0.2) return 6;\n if (divergence < 0.5) return 4;\n return 2;\n}\n\nfunction scoreProtocol(vault: Vault): number {\n return PROTOCOL_TIERS[vault.protocol.name] ?? 3;\n}\n","// SPDX-License-Identifier: Apache-2.0\nimport type { VaultListParams } from './clients/index.js';\n\nexport type StrategyPreset = 'conservative' | 'max-apy' | 'diversified' | 'risk-adjusted';\n\nexport interface StrategyConfig {\n name: StrategyPreset;\n description: string;\n filters: Partial<VaultListParams> & {\n minTvlUsd?: number;\n tags?: string[];\n protocols?: string[];\n minRiskScore?: number;\n };\n sort: 'apy' | 'tvl' | 'risk';\n sortDirection: 'asc' | 'desc';\n}\n\nconst BLUE_CHIP_PROTOCOLS = ['aave-v3', 'morpho-v1', 'euler-v2', 'pendle', 'maple'];\n\nexport const STRATEGIES: Record<StrategyPreset, StrategyConfig> = {\n conservative: {\n name: 'conservative',\n description: 'Stablecoin-tagged, TVL > $50M, APY 3-7%, blue-chip protocols only',\n filters: {\n tags: ['stablecoin'],\n minTvlUsd: 50_000_000,\n protocols: BLUE_CHIP_PROTOCOLS,\n },\n sort: 'apy',\n sortDirection: 'desc',\n },\n\n 'max-apy': {\n name: 'max-apy',\n description: 'Sort by APY descending, no TVL floor',\n filters: {},\n sort: 'apy',\n sortDirection: 'desc',\n },\n\n diversified: {\n name: 'diversified',\n description: 'Spread across 3+ chains, 3+ protocols, mix of stablecoin + LST',\n filters: {\n minTvlUsd: 1_000_000,\n },\n sort: 'apy',\n sortDirection: 'desc',\n },\n\n 'risk-adjusted': {\n name: 'risk-adjusted',\n description: 'Filter by risk score >= 7, then sort by APY',\n filters: {\n minRiskScore: 7,\n },\n sort: 'apy',\n sortDirection: 'desc',\n },\n};\n\nexport function getStrategy(preset: StrategyPreset): StrategyConfig {\n return STRATEGIES[preset];\n}\n","// SPDX-License-Identifier: Apache-2.0\nimport type { Vault } from './schemas/index.js';\nimport { parseTvl } from './schemas/vault.js';\nimport { riskScore, type RiskScore } from './risk-scorer.js';\nimport { STRATEGIES, type StrategyPreset } from './strategies.js';\n\nexport interface SuggestParams {\n amount: number;\n asset?: string;\n maxChains?: number;\n strategy?: StrategyPreset;\n maxVaults?: number;\n}\n\nexport interface Allocation {\n vault: Vault;\n risk: RiskScore;\n percentage: number;\n amount: number;\n apy: number;\n}\n\nexport interface SuggestResult {\n totalAmount: number;\n expectedApy: number;\n allocations: Allocation[];\n}\n\n/**\n * Portfolio allocation engine.\n * Uses a risk-adjusted score: score = apy / (11 - riskScore)\n * to weight allocations proportionally.\n * Enforces maxChains diversification constraint.\n */\nexport function suggest(vaults: Vault[], params: SuggestParams): SuggestResult {\n const maxVaults = params.maxVaults ?? 5;\n const maxChains = params.maxChains ?? 5;\n\n // Filter by asset if specified\n let candidates = params.asset\n ? vaults.filter((v) =>\n v.underlyingTokens.some(\n (t) => t.symbol.toUpperCase() === params.asset?.toUpperCase(),\n ),\n )\n : [...vaults];\n\n // Only transactional vaults\n candidates = candidates.filter((v) => v.isTransactional);\n\n // Apply strategy filters if specified\n if (params.strategy) {\n const strategy = STRATEGIES[params.strategy];\n if (strategy) {\n if (strategy.filters.minTvlUsd) {\n const minTvl = strategy.filters.minTvlUsd;\n candidates = candidates.filter((v) => parseTvl(v.analytics.tvl).parsed >= minTvl);\n }\n if (strategy.filters.tags?.length) {\n const requiredTags = strategy.filters.tags;\n candidates = candidates.filter((v) => requiredTags.some((t) => v.tags.includes(t)));\n }\n if (strategy.filters.protocols?.length) {\n const allowedProtocols = strategy.filters.protocols;\n candidates = candidates.filter((v) => allowedProtocols.includes(v.protocol.name));\n }\n if (strategy.filters.minRiskScore) {\n const minScore = strategy.filters.minRiskScore;\n candidates = candidates.filter((v) => riskScore(v).score >= minScore);\n }\n }\n }\n\n // Score each candidate\n const scored = candidates.map((vault) => {\n const risk = riskScore(vault);\n const apy = vault.analytics.apy.total;\n // Higher APY + higher risk score = better allocation candidate\n const allocationScore = apy * (risk.score / 10);\n return { vault, risk, apy, allocationScore };\n });\n\n // Sort by allocation score descending\n scored.sort((a, b) => b.allocationScore - a.allocationScore);\n\n // Enforce maxChains constraint\n const selectedChains = new Set<number>();\n const selected: typeof scored = [];\n\n for (const item of scored) {\n if (selected.length >= maxVaults) break;\n if (selectedChains.size >= maxChains && !selectedChains.has(item.vault.chainId)) {\n continue;\n }\n selectedChains.add(item.vault.chainId);\n selected.push(item);\n }\n\n if (selected.length === 0) {\n return { totalAmount: params.amount, expectedApy: 0, allocations: [] };\n }\n\n // Allocate proportionally by score\n const totalScore = selected.reduce((sum, s) => sum + s.allocationScore, 0);\n\n const allocations: Allocation[] = selected.map((s) => {\n const percentage = totalScore > 0 ? (s.allocationScore / totalScore) * 100 : 100 / selected.length;\n return {\n vault: s.vault,\n risk: s.risk,\n percentage: Math.round(percentage * 10) / 10,\n amount: Math.round((percentage / 100) * params.amount * 100) / 100,\n apy: s.apy,\n };\n });\n\n const expectedApy =\n allocations.reduce((sum, a) => sum + a.apy * (a.percentage / 100), 0);\n\n return {\n totalAmount: params.amount,\n expectedApy: Math.round(expectedApy * 100) / 100,\n allocations,\n };\n}\n","// SPDX-License-Identifier: Apache-2.0\nimport type { ComposerClient } from './clients/index.js';\nimport type { Vault, QuoteResponse } from './schemas/index.js';\nimport { toSmallestUnit } from './build-deposit-quote.js';\n\nexport interface GasRoute {\n fromChain: number;\n fromChainName: string;\n quote: QuoteResponse;\n totalCostUsd: number;\n gasCostUsd: number;\n feeCostUsd: number;\n executionDuration: number;\n}\n\nexport interface GasOptimizeOptions {\n fromAmount: string;\n wallet: string;\n /** Token address on the vault's chain (used for same-chain route) */\n fromToken?: string;\n /** Map of chainId → token address for cross-chain routes */\n fromTokens?: Record<number, string>;\n fromChains?: number[];\n fromAmountForGas?: string;\n}\n\nconst CHAIN_NAMES: Record<number, string> = {\n 1: 'Ethereum',\n 10: 'Optimism',\n 56: 'BSC',\n 100: 'Gnosis',\n 130: 'Unichain',\n 137: 'Polygon',\n 143: 'Monad',\n 146: 'Sonic',\n 5000: 'Mantle',\n 8453: 'Base',\n 42161: 'Arbitrum',\n 42220: 'Celo',\n 43114: 'Avalanche',\n 59144: 'Linea',\n 80094: 'Berachain',\n 747474: 'Katana',\n};\n\n/**\n * Compare deposit routes from multiple source chains.\n * Returns routes sorted by total cost (cheapest first).\n * Integrates LI.Fuel via fromAmountForGas parameter.\n */\nexport async function optimizeGasRoutes(\n vault: Vault,\n composer: ComposerClient,\n options: GasOptimizeOptions,\n): Promise<GasRoute[]> {\n const fromChains = options.fromChains ?? [vault.chainId];\n const decimals = vault.underlyingTokens[0]?.decimals ?? 18;\n const rawAmount = toSmallestUnit(options.fromAmount, decimals);\n\n const defaultFromToken =\n options.fromToken ?? vault.underlyingTokens[0]?.address;\n\n if (!defaultFromToken && !options.fromTokens) return [];\n\n const routePromises = fromChains.map(async (fromChain): Promise<GasRoute | null> => {\n try {\n // For cross-chain routes, use the chain-specific token address if available\n const fromToken =\n options.fromTokens?.[fromChain] ??\n (fromChain === vault.chainId ? defaultFromToken : undefined);\n\n if (!fromToken) return null; // Skip chains without a known fromToken\n\n const quote = await composer.getQuote({\n fromChain,\n toChain: vault.chainId,\n fromToken,\n toToken: vault.address,\n fromAddress: options.wallet,\n toAddress: options.wallet,\n fromAmount: rawAmount,\n fromAmountForGas: options.fromAmountForGas,\n });\n\n const gasCostUsd = (quote.estimate.gasCosts ?? []).reduce(\n (sum, g) => sum + Number(g.amountUSD),\n 0,\n );\n const feeCostUsd = (quote.estimate.feeCosts ?? []).reduce(\n (sum, f) => sum + Number(f.amountUSD),\n 0,\n );\n\n return {\n fromChain,\n fromChainName: CHAIN_NAMES[fromChain] ?? `Chain ${fromChain}`,\n quote,\n totalCostUsd: gasCostUsd + feeCostUsd,\n gasCostUsd,\n feeCostUsd,\n executionDuration: quote.estimate.executionDuration,\n };\n } catch {\n return null;\n }\n });\n\n const results = await Promise.all(routePromises);\n return results\n .filter((r): r is GasRoute => r !== null)\n .sort((a, b) => a.totalCostUsd - b.totalCostUsd);\n}\n","// SPDX-License-Identifier: Apache-2.0\nimport type { EarnDataClient } from './clients/index.js';\nimport type { Vault } from './schemas/index.js';\nimport { parseTvl } from './schemas/vault.js';\n\nexport interface WatchOptions {\n apyDropPercent?: number;\n tvlDropPercent?: number;\n interval?: number;\n /** AbortSignal to cancel the watcher */\n signal?: AbortSignal;\n /** Maximum number of iterations (0 = unlimited) */\n maxIterations?: number;\n}\n\nexport type WatchEventType = 'apy-drop' | 'tvl-drop' | 'update';\n\nexport interface WatchEvent {\n type: WatchEventType;\n vault: Vault;\n previous: { apy: number; tvlUsd: number } | null;\n current: { apy: number; tvlUsd: number };\n timestamp: Date;\n}\n\n/**\n * Watch a vault for APY/TVL changes.\n * Returns an AsyncGenerator that yields events.\n * Supports cancellation via AbortSignal and maxIterations.\n */\nexport async function* watch(\n client: EarnDataClient,\n vaultSlug: string,\n options: WatchOptions = {},\n): AsyncGenerator<WatchEvent> {\n const interval = options.interval ?? 60_000;\n const apyThreshold = options.apyDropPercent ?? 20;\n const tvlThreshold = options.tvlDropPercent ?? 30;\n const maxIter = options.maxIterations ?? 0;\n\n let previous: { apy: number; tvlUsd: number } | null = null;\n let iteration = 0;\n\n while (true) {\n // Check abort signal\n if (options.signal?.aborted) return;\n\n // Check max iterations\n if (maxIter > 0 && iteration >= maxIter) return;\n iteration++;\n\n const vault = await client.getVaultBySlug(vaultSlug);\n const currentApy = vault.analytics.apy.total;\n const currentTvl = parseTvl(vault.analytics.tvl).parsed;\n\n const current = { apy: currentApy, tvlUsd: currentTvl };\n\n if (previous) {\n // Check APY drop\n if (previous.apy > 0) {\n const apyDrop = ((previous.apy - currentApy) / previous.apy) * 100;\n if (apyDrop >= apyThreshold) {\n yield {\n type: 'apy-drop',\n vault,\n previous,\n current,\n timestamp: new Date(),\n };\n }\n }\n\n // Check TVL drop\n if (previous.tvlUsd > 0) {\n const tvlDrop = ((previous.tvlUsd - currentTvl) / previous.tvlUsd) * 100;\n if (tvlDrop >= tvlThreshold) {\n yield {\n type: 'tvl-drop',\n vault,\n previous,\n current,\n timestamp: new Date(),\n };\n }\n }\n\n yield { type: 'update', vault, previous, current, timestamp: new Date() };\n } else {\n yield { type: 'update', vault, previous: null, current, timestamp: new Date() };\n }\n\n previous = current;\n\n // Abortable sleep\n if (options.signal?.aborted) return;\n await new Promise<void>((resolve) => {\n const timer = setTimeout(resolve, interval);\n options.signal?.addEventListener('abort', () => {\n clearTimeout(timer);\n resolve();\n }, { once: true });\n });\n }\n}\n","// SPDX-License-Identifier: Apache-2.0\nimport type { Vault } from './schemas/index.js';\nimport { LRUCache } from './cache.js';\n\nexport interface ApyDataPoint {\n timestamp: string;\n apy: number;\n tvlUsd: number;\n}\n\nexport interface DeFiLlamaPool {\n pool: string;\n chain: string;\n project: string;\n symbol: string;\n tvlUsd: number;\n apy: number;\n underlyingTokens: string[] | null;\n stablecoin: boolean;\n}\n\n/**\n * DeFiLlama protocol name mapping.\n * LI.FI uses names like \"morpho-v1\", DeFiLlama uses the same.\n * Some need explicit mapping.\n */\nconst LIFI_TO_LLAMA_PROJECT: Record<string, string> = {\n 'aave-v3': 'aave-v3',\n 'morpho-v1': 'morpho-v1',\n 'euler-v2': 'euler-v2',\n 'pendle': 'pendle',\n 'maple': 'maple-finance',\n 'ethena-usde': 'ethena',\n 'ether.fi-liquid': 'ether.fi',\n 'ether.fi-stake': 'ether.fi-stake',\n 'upshift': 'upshift',\n 'neverland': 'neverland',\n 'yo-protocol': 'yo-protocol',\n};\n\nconst CHAIN_ID_TO_LLAMA: Record<number, string> = {\n 1: 'Ethereum',\n 10: 'Optimism',\n 56: 'BSC',\n 100: 'Gnosis',\n 130: 'Unichain',\n 137: 'Polygon',\n 143: 'Monad',\n 146: 'Sonic',\n 5000: 'Mantle',\n 8453: 'Base',\n 42161: 'Arbitrum',\n 42220: 'Celo',\n 43114: 'Avalanche',\n 59144: 'Linea',\n 80094: 'Berachain',\n 747474: 'Katana',\n};\n\n// Cache the full pools list — it's 10MB+ and changes infrequently\nconst poolsCache = new LRUCache<DeFiLlamaPool[]>({ ttl: 3_600_000, maxSize: 1 });\n\n/**\n * Fetch the DeFiLlama pools list with caching (1 hour TTL).\n * The pools endpoint returns ~16K pools, ~10MB. We cache it to avoid\n * fetching it on every call.\n */\nasync function fetchPools(): Promise<DeFiLlamaPool[]> {\n const cached = poolsCache.get('all');\n if (cached) return cached;\n\n const res = await globalThis.fetch('https://yields.llama.fi/pools');\n if (!res.ok) return [];\n\n const data = (await res.json()) as { data: DeFiLlamaPool[] };\n const pools = data.data ?? [];\n poolsCache.set('all', pools);\n return pools;\n}\n\n/**\n * Match a LI.FI vault to a DeFiLlama pool.\n *\n * DeFiLlama pools have UUID IDs (not addresses). Matching strategy:\n * 1. Filter by project name (LI.FI \"morpho-v1\" → DeFiLlama \"morpho-v1\")\n * 2. Filter by chain name\n * 3. Filter by underlying token address (the deposit token)\n * 4. Match by symbol (vault name)\n * 5. If multiple matches, pick the one with closest TVL to the LI.FI vault\n */\nfunction matchPool(\n vault: Vault,\n pools: DeFiLlamaPool[],\n): DeFiLlamaPool | null {\n const chainName = CHAIN_ID_TO_LLAMA[vault.chainId];\n if (!chainName) return null;\n\n const llamaProject = LIFI_TO_LLAMA_PROJECT[vault.protocol.name];\n if (!llamaProject) return null;\n\n // Filter by project + chain\n let candidates = pools.filter(\n (p) => p.project === llamaProject && p.chain === chainName,\n );\n\n if (candidates.length === 0) return null;\n\n // Filter by underlying token if available\n if (vault.underlyingTokens.length > 0) {\n const underlyingAddr = vault.underlyingTokens[0]!.address.toLowerCase();\n const withToken = candidates.filter(\n (p) =>\n p.underlyingTokens?.some(\n (t) => t.toLowerCase() === underlyingAddr,\n ),\n );\n if (withToken.length > 0) candidates = withToken;\n }\n\n // Try to narrow by symbol match\n const vaultName = vault.name.toUpperCase();\n const bySymbol = candidates.filter(\n (p) => p.symbol.toUpperCase() === vaultName,\n );\n if (bySymbol.length > 0) candidates = bySymbol;\n\n if (candidates.length === 0) return null;\n\n // Pick the one with closest TVL to our vault\n const vaultTvl = Number(vault.analytics.tvl.usd);\n candidates.sort(\n (a, b) => Math.abs(a.tvlUsd - vaultTvl) - Math.abs(b.tvlUsd - vaultTvl),\n );\n\n return candidates[0] ?? null;\n}\n\n/**\n * Fetch 30-day APY history from DeFiLlama's free yields API.\n *\n * Properly matches LI.FI vaults to DeFiLlama pools using:\n * project name + chain + underlying tokens + symbol + TVL proximity.\n *\n * Caches the 10MB+ pools list for 1 hour to avoid repeated fetches.\n */\nexport async function getApyHistory(vault: Vault): Promise<ApyDataPoint[]>;\nexport async function getApyHistory(vaultAddress: string, chainId: number): Promise<ApyDataPoint[]>;\nexport async function getApyHistory(\n vaultOrAddress: Vault | string,\n chainId?: number,\n): Promise<ApyDataPoint[]> {\n try {\n const pools = await fetchPools();\n if (pools.length === 0) return [];\n\n let pool: DeFiLlamaPool | null = null;\n\n if (typeof vaultOrAddress === 'string') {\n // Legacy signature: address + chainId — less accurate matching\n const chainName = CHAIN_ID_TO_LLAMA[chainId!];\n if (!chainName) return [];\n const addr = vaultOrAddress.toLowerCase();\n pool = pools.find(\n (p) =>\n p.chain === chainName &&\n p.underlyingTokens?.some((t) => t.toLowerCase() === addr),\n ) ?? null;\n } else {\n // Full vault object — accurate matching\n pool = matchPool(vaultOrAddress, pools);\n }\n\n if (!pool) return [];\n\n // Fetch chart data for the matched pool\n const chartRes = await globalThis.fetch(\n `https://yields.llama.fi/chart/${pool.pool}`,\n );\n if (!chartRes.ok) return [];\n\n const chartData = (await chartRes.json()) as {\n data: Array<{ timestamp: string; apy: number; tvlUsd: number }>;\n };\n return (chartData.data ?? []).slice(-30).map((d) => ({\n timestamp: d.timestamp,\n apy: d.apy ?? 0,\n tvlUsd: d.tvlUsd ?? 0,\n }));\n } catch {\n return [];\n }\n}\n","// SPDX-License-Identifier: Apache-2.0\n\n/**\n * ERC-20 allowance checking and approval transaction building.\n *\n * Uses the standard ERC-20 ABI for allowance() and approve().\n * Works with any EVM JSON-RPC provider via raw fetch — no viem dependency required.\n * The approval address comes from the Composer quote's estimate.approvalAddress.\n */\n\nexport interface AllowanceResult {\n allowance: bigint;\n sufficient: boolean;\n requiredAmount: bigint;\n}\n\nexport interface ApprovalTx {\n to: string;\n data: string;\n value: '0x0';\n chainId: number;\n}\n\n// ERC-20 function signatures\nconst ALLOWANCE_SELECTOR = '0xdd62ed3e'; // allowance(address,address)\nconst APPROVE_SELECTOR = '0x095ea7b3'; // approve(address,uint256)\n\n/**\n * Check the ERC-20 allowance for a token.\n *\n * @param rpcUrl - JSON-RPC endpoint for the chain\n * @param tokenAddress - ERC-20 token contract address\n * @param owner - Wallet address (token holder)\n * @param spender - Address to check allowance for (from quote.estimate.approvalAddress)\n * @param requiredAmount - Amount needed in smallest unit\n */\nexport async function checkAllowance(\n rpcUrl: string,\n tokenAddress: string,\n owner: string,\n spender: string,\n requiredAmount: bigint,\n): Promise<AllowanceResult> {\n // Encode allowance(owner, spender) call\n const ownerPadded = owner.slice(2).toLowerCase().padStart(64, '0');\n const spenderPadded = spender.slice(2).toLowerCase().padStart(64, '0');\n const calldata = `${ALLOWANCE_SELECTOR}${ownerPadded}${spenderPadded}`;\n\n const res = await globalThis.fetch(rpcUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n jsonrpc: '2.0',\n method: 'eth_call',\n params: [{ to: tokenAddress, data: calldata }, 'latest'],\n id: 1,\n }),\n });\n\n const json = (await res.json()) as { result?: string; error?: { message: string } };\n if (json.error || !json.result) {\n return { allowance: 0n, sufficient: false, requiredAmount };\n }\n\n const allowance = BigInt(json.result);\n return {\n allowance,\n sufficient: allowance >= requiredAmount,\n requiredAmount,\n };\n}\n\n/**\n * Build an ERC-20 approve transaction.\n *\n * @param tokenAddress - ERC-20 token contract\n * @param spender - Address to approve (from quote.estimate.approvalAddress)\n * @param amount - Amount to approve in smallest unit (use MaxUint256 for unlimited)\n * @param chainId - Chain ID for the transaction\n */\nexport function buildApprovalTx(\n tokenAddress: string,\n spender: string,\n amount: bigint,\n chainId: number,\n): ApprovalTx {\n const spenderPadded = spender.slice(2).toLowerCase().padStart(64, '0');\n const amountHex = amount.toString(16).padStart(64, '0');\n const data = `${APPROVE_SELECTOR}${spenderPadded}${amountHex}`;\n\n return {\n to: tokenAddress,\n data,\n value: '0x0',\n chainId,\n };\n}\n\n/** MaxUint256 for unlimited approval */\nexport const MAX_UINT256 = 2n ** 256n - 1n;\n","// SPDX-License-Identifier: Apache-2.0\n\n// ── Re-exports ──\nexport * from './schemas/index.js';\nexport * from './errors.js';\nexport * from './rate-limiter.js';\nexport * from './cache.js';\nexport * from './retry.js';\nexport { EarnDataClient, type EarnDataClientOptions, type VaultListParams } from './clients/index.js';\nexport { ComposerClient, type ComposerClientOptions, type QuoteParams } from './clients/index.js';\nexport { buildDepositQuote, toSmallestUnit, fromSmallestUnit, type DepositQuoteOptions, type DepositQuoteResult } from './build-deposit-quote.js';\nexport { buildRedeemQuote, type RedeemQuoteOptions, type RedeemQuoteResult } from './build-redeem-quote.js';\nexport { preflight, type PreflightReport, type PreflightOptions } from './preflight.js';\nexport { riskScore, type RiskScore, type RiskBreakdown } from './risk-scorer.js';\nexport { suggest, type SuggestParams, type SuggestResult, type Allocation } from './suggest.js';\nexport { STRATEGIES, getStrategy, type StrategyPreset, type StrategyConfig } from './strategies.js';\nexport { optimizeGasRoutes, type GasRoute, type GasOptimizeOptions } from './gas-optimizer.js';\nexport { watch, type WatchOptions, type WatchEvent, type WatchEventType } from './watch.js';\nexport { getApyHistory, type ApyDataPoint } from './apy-history.js';\nexport { parseTvl, getBestApy, type TvlParsed } from './schemas/vault.js';\nexport { checkAllowance, buildApprovalTx, MAX_UINT256, type AllowanceResult, type ApprovalTx } from './allowance.js';\n\n// ── Factory ──\nimport { EarnDataClient, type EarnDataClientOptions } from './clients/index.js';\nimport { ComposerClient } from './clients/index.js';\nimport { buildDepositQuote, type DepositQuoteOptions } from './build-deposit-quote.js';\nimport { buildRedeemQuote, type RedeemQuoteOptions } from './build-redeem-quote.js';\nimport { preflight, type PreflightOptions, type PreflightReport } from './preflight.js';\nimport { riskScore, type RiskScore } from './risk-scorer.js';\nimport { suggest, type SuggestParams, type SuggestResult } from './suggest.js';\nimport { STRATEGIES, type StrategyPreset } from './strategies.js';\nimport { optimizeGasRoutes, type GasOptimizeOptions, type GasRoute } from './gas-optimizer.js';\nimport { watch, type WatchOptions, type WatchEvent } from './watch.js';\nimport { getApyHistory, type ApyDataPoint } from './apy-history.js';\nimport { parseTvl } from './schemas/vault.js';\nimport type { Vault, Chain, ProtocolDetail, PortfolioResponse, VaultListResponse } from './schemas/index.js';\n\nexport interface EarnForgeOptions {\n composerApiKey?: string;\n earnData?: EarnDataClientOptions;\n composerBaseUrl?: string;\n cache?: { ttl?: number; maxSize?: number };\n}\n\nexport interface EarnForge {\n vaults: {\n list: (params?: VaultListQueryParams) => Promise<VaultListResponse>;\n listAll: (params?: Omit<VaultListQueryParams, 'cursor'>) => AsyncIterable<Vault>;\n get: (slug: string) => Promise<Vault>;\n top: (params?: TopVaultsParams) => Promise<Vault[]>;\n };\n chains: {\n list: () => Promise<Chain[]>;\n };\n protocols: {\n list: () => Promise<ProtocolDetail[]>;\n };\n portfolio: {\n get: (wallet: string) => Promise<PortfolioResponse>;\n };\n buildDepositQuote: (vault: Vault, options: DepositQuoteOptions) => Promise<import('./build-deposit-quote.js').DepositQuoteResult>;\n buildRedeemQuote: (vault: Vault, options: RedeemQuoteOptions) => Promise<import('./build-redeem-quote.js').RedeemQuoteResult>;\n preflight: (vault: Vault, wallet: string, options?: PreflightOptions) => PreflightReport;\n riskScore: (vault: Vault) => RiskScore;\n suggest: (params: SuggestParams & { vaults?: Vault[] }) => Promise<SuggestResult>;\n optimizeGasRoutes: (vault: Vault, options: GasOptimizeOptions) => Promise<GasRoute[]>;\n watch: (vaultSlug: string, options?: WatchOptions) => AsyncGenerator<WatchEvent>;\n getApyHistory: {\n (vault: Vault): Promise<ApyDataPoint[]>;\n (vaultAddress: string, chainId: number): Promise<ApyDataPoint[]>;\n };\n earnDataClient: EarnDataClient;\n composerClient: ComposerClient | null;\n}\n\ninterface VaultListQueryParams {\n chainId?: number;\n asset?: string;\n minTvl?: number;\n sortBy?: string;\n cursor?: string;\n strategy?: StrategyPreset;\n}\n\ninterface TopVaultsParams {\n asset?: string;\n chainId?: number;\n limit?: number;\n strategy?: StrategyPreset;\n minTvl?: number;\n}\n\n/**\n * Create an EarnForge instance — the main entry point.\n *\n * ```ts\n * const forge = createEarnForge({ composerApiKey: process.env.LIFI_API_KEY });\n * for await (const vault of forge.vaults.listAll({ chainId: 8453 })) {\n * console.log(vault.name, vault.analytics.apy.total);\n * }\n * ```\n */\nexport function createEarnForge(options: EarnForgeOptions = {}): EarnForge {\n const earnData = new EarnDataClient({\n cache: options.cache,\n ...options.earnData,\n });\n\n const composer = options.composerApiKey\n ? new ComposerClient({\n apiKey: options.composerApiKey,\n baseUrl: options.composerBaseUrl,\n })\n : null;\n\n function requireComposer(): ComposerClient {\n if (!composer) {\n throw new Error(\n 'Composer API key required. Pass composerApiKey to createEarnForge() or set LIFI_API_KEY.',\n );\n }\n return composer;\n }\n\n async function getTopVaults(params: TopVaultsParams = {}): Promise<Vault[]> {\n const limit = params.limit ?? 10;\n const strategy = params.strategy ? STRATEGIES[params.strategy] : null;\n const vaults: Vault[] = [];\n\n for await (const vault of earnData.listAllVaults({\n chainId: params.chainId,\n asset: params.asset,\n minTvl: params.minTvl,\n })) {\n // Apply strategy filters\n if (strategy) {\n const tvlUsd = parseTvl(vault.analytics.tvl).parsed;\n if (strategy.filters.minTvlUsd && tvlUsd < strategy.filters.minTvlUsd) continue;\n if (strategy.filters.tags && !strategy.filters.tags.some((t) => vault.tags.includes(t))) continue;\n if (strategy.filters.protocols && !strategy.filters.protocols.includes(vault.protocol.name)) continue;\n if (strategy.filters.minRiskScore && riskScore(vault).score < strategy.filters.minRiskScore) continue;\n }\n\n vaults.push(vault);\n if (vaults.length >= limit * 3) break; // Fetch extra for sorting\n }\n\n // Sort\n vaults.sort((a, b) => b.analytics.apy.total - a.analytics.apy.total);\n\n return vaults.slice(0, limit);\n }\n\n return {\n vaults: {\n list: (params) => earnData.listVaults(params),\n listAll: (params) => earnData.listAllVaults(params),\n get: (slug) => earnData.getVaultBySlug(slug),\n top: getTopVaults,\n },\n chains: {\n list: () => earnData.listChains(),\n },\n protocols: {\n list: () => earnData.listProtocols(),\n },\n portfolio: {\n get: (wallet) => earnData.getPortfolio(wallet),\n },\n buildDepositQuote: (vault, opts) => buildDepositQuote(vault, opts, requireComposer()),\n buildRedeemQuote: (vault, opts) => buildRedeemQuote(vault, opts, requireComposer()),\n preflight: (vault, wallet, opts) => preflight(vault, wallet, opts),\n riskScore: (vault) => riskScore(vault),\n suggest: async (params) => {\n const vaults = params.vaults ?? [];\n if (vaults.length === 0) {\n // Fetch vaults if not provided\n const allVaults: Vault[] = [];\n for await (const v of earnData.listAllVaults({ asset: params.asset })) {\n allVaults.push(v);\n }\n return suggest(allVaults, params);\n }\n return suggest(vaults, params);\n },\n optimizeGasRoutes: (vault, opts) => optimizeGasRoutes(vault, requireComposer(), opts),\n watch: (slug, opts) => watch(earnData, slug, opts),\n getApyHistory,\n earnDataClient: earnData,\n composerClient: composer,\n };\n}\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@earnforge/sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"description": "Typed SDK for the LI.FI Earn API — vault discovery, risk scoring, yield strategies, and deposit quoting with all 18 pitfalls handled by default",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"lifi",
|
|
9
|
+
"earn",
|
|
10
|
+
"defi",
|
|
11
|
+
"yield",
|
|
12
|
+
"vaults",
|
|
13
|
+
"sdk"
|
|
14
|
+
],
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"import": "./dist/esm/index.mjs",
|
|
18
|
+
"require": "./dist/cjs/index.cjs",
|
|
19
|
+
"types": "./dist/esm/index.d.mts"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"main": "./dist/cjs/index.cjs",
|
|
23
|
+
"module": "./dist/esm/index.mjs",
|
|
24
|
+
"types": "./dist/esm/index.d.mts",
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"README.md",
|
|
28
|
+
"LICENSE"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsdown src/index.ts --format esm,cjs --dts --out-dir dist/esm && tsdown src/index.ts --format cjs --out-dir dist/cjs",
|
|
32
|
+
"typecheck": "tsc --noEmit",
|
|
33
|
+
"test": "vitest run",
|
|
34
|
+
"test:unit": "vitest run --exclude '**/*.live.test.ts'",
|
|
35
|
+
"test:live": "vitest run --include '**/*.live.test.ts'",
|
|
36
|
+
"circular": "madge --circular --extensions ts src/",
|
|
37
|
+
"clean": "rm -rf dist .turbo"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"zod": "^3.24.4"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/node": "^25.6.0",
|
|
44
|
+
"@vitest/coverage-v8": "^4.1.4",
|
|
45
|
+
"madge": "^8.0.0",
|
|
46
|
+
"msw": "^2.13.2",
|
|
47
|
+
"tsdown": "^0.21.7",
|
|
48
|
+
"typescript": "^5.9.3",
|
|
49
|
+
"vitest": "^4.1.4"
|
|
50
|
+
}
|
|
51
|
+
}
|