UNPKG

@atomiqlabs/sdk

Version:

atomiq labs SDK for cross-chain swaps between smart chains and bitcoin

230 lines (215 loc) 8.07 kB
import {BitcoinTokens, Token} from "../types/Token"; import {TokenAmount, toTokenAmount} from "../types/TokenAmount"; import {LNURLPay, LNURLPayParamsWithUrl} from "../types/lnurl/LNURLPay"; import {LNURLWithdraw, LNURLWithdrawParamsWithUrl} from "../types/lnurl/LNURLWithdraw"; import {parseApiInput} from "./ApiParser"; import {Swapper} from "../swapper/Swapper"; /** * Unified amount type for all API responses * * @category API */ export type ApiAmount = { /** Decimal format of the amount, e.g. "1.5" */ amount: string; /** Raw base units as string, e.g. "1500000000000000000" */ rawAmount: string; /** Token decimals, e.g. 18 */ decimals: number; /** Token ticker, e.g. "STRK" */ symbol: string; /** Chain identifier, e.g. "STARKNET", "BITCOIN", "LIGHTNING" */ chain: string; } /** * Serializable token representation for API responses * * @category API */ export type ApiToken = { /** Canonical token identifier accepted by the API, e.g. "BITCOIN-BTC", "LIGHTNING-BTC", "STARKNET-STRK" */ id: string; /** Chain identifier, e.g. "STARKNET", "BITCOIN", "LIGHTNING" */ chainId: string; /** Token ticker, e.g. "STRK" */ ticker: string; /** Full token name */ name: string; /** Decimal places of the token */ decimals: number; /** Token contract address, or empty string for BTC on Bitcoin/Lightning */ address: string; } /** * Serializable LNURL-pay representation for API responses * * @category API */ export type ApiLNURLPay = { /** Marks the LNURL payload as an LNURL-pay response. */ type: "pay"; /** Minimum payable amount supported by the LNURL-pay endpoint. */ min: ApiAmount; /** Maximum payable amount supported by the LNURL-pay endpoint. */ max: ApiAmount; /** Maximum comment length accepted by the LNURL-pay endpoint. */ commentMaxLength: number; /** Short human-readable description of the payee, when provided by the LNURL service. */ shortDescription?: string; /** Longer human-readable description of the payee, when provided by the LNURL service. */ longDescription?: string; /** Optional icon for the payee, usually encoded as a data URL. */ icon?: string; /** Raw LNURL-pay metadata and callback parameters. */ params: LNURLPayParamsWithUrl; } /** * Serializable LNURL-withdraw representation for API responses * * @category API */ export type ApiLNURLWithdraw = { /** Marks the LNURL payload as an LNURL-withdraw response. */ type: "withdraw"; /** Minimum withdrawable amount supported by the LNURL-withdraw endpoint. */ min: ApiAmount; /** Maximum withdrawable amount supported by the LNURL-withdraw endpoint. */ max: ApiAmount; /** Raw LNURL-withdraw metadata and callback parameters. */ params: LNURLWithdrawParamsWithUrl; } /** * Serializable LNURL representation for API responses * * @category API */ export type ApiLNURL = ApiLNURLPay | ApiLNURLWithdraw; /** * Converts a TokenAmount to the serializable ApiAmount format * * @category API */ export function toApiAmount(tokenAmount: TokenAmount): ApiAmount { return { amount: tokenAmount.amount, rawAmount: tokenAmount.rawAmount != null ? tokenAmount.rawAmount.toString() : "0", decimals: tokenAmount.token.decimals, symbol: tokenAmount.token.ticker, chain: tokenAmount.token.chainId }; } /** * Converts a Token to the serializable ApiToken format * * @category API */ export function toApiToken(token: Token): ApiToken { return { id: `${token.chainId}-${token.ticker}`, chainId: token.chainId, ticker: token.ticker, name: token.name, decimals: token.decimals, address: token.address }; } /** * Converts LNURL data to the serializable API format * * @category API */ export function toApiLNURL(lnurl: LNURLPay | LNURLWithdraw, swapper: Swapper<any>): ApiLNURL { if(lnurl.type === "pay") { return { type: "pay", min: toApiAmount(toTokenAmount(lnurl.min, BitcoinTokens.BTCLN, swapper.prices)), max: toApiAmount(toTokenAmount(lnurl.max, BitcoinTokens.BTCLN, swapper.prices)), commentMaxLength: lnurl.commentMaxLength, ...(lnurl.shortDescription != null ? {shortDescription: lnurl.shortDescription} : {}), ...(lnurl.longDescription != null ? {longDescription: lnurl.longDescription} : {}), ...(lnurl.icon != null ? {icon: lnurl.icon} : {}), params: lnurl.params }; } return { type: "withdraw", min: toApiAmount(toTokenAmount(lnurl.min, BitcoinTokens.BTCLN, swapper.prices)), max: toApiAmount(toTokenAmount(lnurl.max, BitcoinTokens.BTCLN, swapper.prices)), params: lnurl.params }; } /** * Maps a TypeScript type to its schema type string representation * * @category API */ type TypeToSchemaType<T> = NonNullable<T> extends string ? "string" : NonNullable<T> extends number ? "number" : NonNullable<T> extends bigint ? "bigint" : NonNullable<T> extends boolean ? "boolean" : NonNullable<T> extends any[] ? "array" : "object"; /** * Schema definition for a single API input field. * * @category API */ export type InputSchemaField<T = unknown> = { /** Primitive schema type inferred from the TypeScript field type. */ type: TypeToSchemaType<T>; /** Whether the field is required by the endpoint input parser. */ required: boolean; /** Human-readable description of the field exposed by the API schema. */ description: string; /** Nested schema properties for object-like fields. */ properties?: T extends readonly any[] ? never : T extends object ? { [K in keyof T]-?: InputSchemaField<T[K]>; } : never; // Specifies nested object properties /** Schema definition for array items when the field is an array. */ items?: T extends readonly (infer U)[] ? InputSchemaField<U> : never; // Specifies type of the array items /** Enumerated allowed values for string, number, or bigint fields when constrained. */ allowedValues?: NonNullable<T> extends string | number | bigint ? NonNullable<T>[] : never; // An array of allowed values for a given field }; /** * Schema definition describing the accepted input shape for an API endpoint. * * @category API */ export type InputSchema<TInput> = { [K in keyof TInput]-?: InputSchemaField<TInput[K]>; }; /** * Typed API endpoint definition for framework-agnostic integration * * @category API */ export type ApiEndpoint<TInput, TOutput, Type extends "GET" | "POST"> = { /** HTTP method used by the endpoint. */ type: Type; /** Human-readable description of what this endpoint does, written for AI agent consumption. */ description: string; /** Structured schema describing the accepted input payload. */ inputSchema: InputSchema<TInput>; /** Typed endpoint implementation that receives already-validated input. */ callback: (input: TInput, abortSignal?: AbortSignal) => Promise<TOutput>; /** Raw endpoint implementation that parses unknown input into the typed callback. */ callbackRaw: (input: unknown, abortSignal?: AbortSignal) => Promise<TOutput>; } export function createApiEndpoint<TInput, TOutput, Type extends "GET" | "POST">( type: Type, description: string, callback: (input: TInput, abortSignal?: AbortSignal) => Promise<TOutput>, inputSchema: InputSchema<TInput> ): ApiEndpoint<TInput, TOutput, Type> { return { type, description, callback, inputSchema, callbackRaw: (input, abortSignal?: AbortSignal) => { return callback(parseApiInput(inputSchema, input), abortSignal); } } }