@olympex-io/olympex-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/LICENSE +11 -0
- package/README.md +157 -0
- package/dist/index.d.mts +824 -0
- package/dist/index.d.ts +824 -0
- package/dist/index.js +863 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +817 -0
- package/dist/index.mjs.map +1 -0
- package/docs/authentication.md +166 -0
- package/docs/contributing-jsdoc.md +184 -0
- package/docs/errors.md +61 -0
- package/docs/fees.md +64 -0
- package/docs/getting-started.md +107 -0
- package/docs/logging.md +85 -0
- package/docs/methods/README.md +25 -0
- package/docs/methods/create-account.md +62 -0
- package/docs/methods/quote.md +84 -0
- package/docs/methods/swap.md +69 -0
- package/package.json +88 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors/base-error.ts","../src/errors/graphql-error.ts","../src/errors/network-error.ts","../src/errors/redact.ts","../src/client/request-signing.ts","../src/client/graphql-client.ts","../src/errors/config-error.ts","../src/fees/validation.ts","../src/fees/constants.ts","../src/config/backend-url.ts","../src/methods/account.ts","../src/methods/get-version.ts","../src/errors/domain-error.ts","../src/client/domain-response.ts","../src/fees/defaults.ts","../src/fees/serialize.ts","../src/graphql/operations.ts","../src/methods/quote.ts","../src/methods/support-chain.ts","../src/methods/swap.ts","../src/methods/tx-status.ts","../src/config/initialize.ts","../src/logging/console-logger.ts","../src/errors/not-implemented-error.ts"],"sourcesContent":["/**\n * Base error for all SDK-thrown failures. Subclasses map to specific failure modes.\n *\n * @remarks Every SDK error exposes a stable `code` string and optional `metadata`.\n * Catch by `instanceof` or compare `error.code` for transport-agnostic handling.\n * See {@link OlympexConfigError}, {@link OlympexDomainError}, {@link OlympexGraphQLError},\n * {@link OlympexNetworkError}, and {@link OlympexNotImplementedError}.\n *\n * @example\n * ```ts\n * import {\n * OlympexSdkError,\n * OlympexConfigError,\n * OlympexDomainError,\n * } from '@olympex-io/olympex-sdk';\n *\n * try {\n * await client.quote({ mode: 'single-chain', params: { ... } });\n * } catch (error) {\n * if (error instanceof OlympexConfigError) {\n * console.error('Invalid input:', error.code, error.message);\n * } else if (error instanceof OlympexDomainError) {\n * console.error('Backend rejected:', error.code, error.metadata);\n * } else if (error instanceof OlympexSdkError) {\n * console.error('SDK error:', error.code);\n * }\n * }\n * ```\n */\nexport class OlympexSdkError extends Error {\n readonly code: string;\n readonly metadata: Record<string, unknown> | undefined;\n\n constructor(message: string, code = 'OLYMPEX_SDK_ERROR', metadata?: Record<string, unknown>) {\n super(message);\n this.name = new.target.name;\n this.code = code;\n this.metadata = metadata;\n }\n}\n","import { OlympexSdkError } from './base-error.js';\n\n/**\n * Thrown when the GraphQL response contains an `errors[]` array on an otherwise successful HTTP 200.\n *\n * @remarks Extends {@link OlympexSdkError}. Distinct from {@link OlympexNetworkError} — the\n * transport succeeded but the GraphQL layer reported execution or validation failures.\n *\n * @see docs/errors.md\n */\nexport class OlympexGraphQLError extends OlympexSdkError {\n constructor(message: string, metadata?: Record<string, unknown>) {\n super(message, 'OLYMPEX_GRAPHQL_ERROR', metadata);\n }\n}\n","import { OlympexSdkError } from './base-error.js';\n\n/**\n * Thrown on network failures: timeouts, fetch errors, or non-2xx HTTP responses.\n *\n * @remarks Extends {@link OlympexSdkError}. Retry logic should consider exponential backoff;\n * inspect `error.metadata` for status codes when available.\n *\n * @see docs/errors.md\n */\nexport class OlympexNetworkError extends OlympexSdkError {\n constructor(message: string, metadata?: Record<string, unknown>) {\n super(message, 'OLYMPEX_NETWORK_ERROR', metadata);\n }\n}\n","const SENSITIVE_KEY_PATTERN = /api[-_]?key|authorization|cookie|token|secret|password/i;\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nexport function redact<T extends Record<string, unknown>>(value: T): T;\nexport function redact(value: unknown): unknown;\nexport function redact(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((item) => redact(item));\n }\n\n if (!isPlainObject(value)) {\n return value;\n }\n\n return Object.fromEntries(\n Object.entries(value).map(([key, entry]) => [\n key,\n SENSITIVE_KEY_PATTERN.test(key) ? '<REDACTED>' : redact(entry),\n ]),\n );\n}\n","import crypto from 'crypto';\n\nexport interface BuildSignedHeadersInput {\n readonly apiKey: string;\n readonly apiSecret: string;\n readonly passphrase: string;\n readonly body: string;\n readonly timestamp?: string;\n readonly nonce?: string;\n}\n\nexport interface SignedHeaders {\n readonly 'x-api-key-id': string;\n readonly 'x-value-info': string;\n readonly 'x-passphrase': string;\n readonly 'x-signature': string;\n}\n\ninterface SignInput {\n readonly message: string;\n readonly secret: string;\n}\n\n// TODO: esto está duplicado en accounts.ts \"repositorio de backend\" evaluar de movelor a un paquete shared npm\nexport function generateBodySignature(body: string): string {\n const hashBuffer = crypto.createHash('sha256').update(body).digest();\n return hashBuffer.toString('base64url');\n}\n\n// TODO: esto está duplicado en accounts.ts \"repositorio de backend\" evaluar de movelor a un paquete shared npm\nfunction getTimestamp(): string {\n return Math.floor(Date.now() / 1000).toString();\n}\n\n// TODO: esto está duplicado en accounts.ts \"repositorio de backend\" evaluar de movelor a un paquete shared npm\nfunction generateNonce(): string {\n return crypto.randomBytes(12).toString('hex');\n}\n\n// TODO: esto está duplicado en accounts.ts \"repositorio de backend\" evaluar de movelor a un paquete shared npm\nfunction sign({ message, secret }: SignInput): string {\n return crypto.createHmac('sha256', secret).update(message).digest('hex');\n}\n\nexport function buildSignedHeaders({\n apiKey,\n apiSecret,\n passphrase,\n body,\n timestamp,\n nonce,\n}: BuildSignedHeadersInput): SignedHeaders {\n const bodyHash = generateBodySignature(body);\n const resolvedTimestamp = timestamp ?? getTimestamp();\n const resolvedNonce = nonce ?? generateNonce();\n const message = `${resolvedTimestamp}\\n${resolvedNonce}\\n${bodyHash}`;\n const headerData = Buffer.from(message).toString('base64');\n\n return {\n 'x-api-key-id': apiKey,\n 'x-value-info': headerData,\n 'x-passphrase': passphrase,\n 'x-signature': sign({ message, secret: apiSecret }),\n };\n}\n","import { OlympexGraphQLError } from '../errors/graphql-error.js';\nimport { OlympexNetworkError } from '../errors/network-error.js';\nimport { redact } from '../errors/redact.js';\nimport type { OlympexLogger } from '../types/public.js';\nimport { buildSignedHeaders } from './request-signing.js';\n\ninterface GraphQLErrorPayload {\n readonly message: string;\n readonly extensions?: Record<string, unknown>;\n readonly path?: readonly (string | number)[];\n}\n\ninterface GraphQLResponse<TData> {\n readonly data?: TData;\n readonly errors?: readonly GraphQLErrorPayload[];\n}\n\nexport interface GraphQLClientOptions {\n readonly apiKey: string;\n readonly apiSecret: string;\n readonly passphrase: string;\n readonly endpoint: string;\n readonly headers?: Record<string, string> | undefined;\n readonly logger?: OlympexLogger | undefined;\n readonly timeoutMs: number;\n}\n\nexport class OlympexGraphQLClient {\n private readonly apiKey: string;\n private readonly apiSecret: string;\n private readonly passphrase: string;\n private readonly endpoint: string;\n private readonly headers: Record<string, string>;\n private readonly logger: OlympexLogger | undefined;\n private readonly timeoutMs: number;\n\n constructor(options: GraphQLClientOptions) {\n this.apiKey = options.apiKey;\n this.apiSecret = options.apiSecret;\n this.passphrase = options.passphrase;\n this.endpoint = options.endpoint;\n this.headers = options.headers ?? {};\n this.logger = options.logger;\n this.timeoutMs = options.timeoutMs;\n }\n\n async request<TData, TVariables extends object>(\n document: string,\n variables: TVariables,\n ): Promise<TData> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.timeoutMs);\n\n try {\n this.logger?.log(\n 'debug',\n 'Sending Olympex GraphQL request',\n redact({\n endpoint: this.endpoint,\n variables,\n }),\n );\n\n const bodyToFetch = JSON.stringify({ query: document, variables });\n const signedHeaders = buildSignedHeaders({\n apiKey: this.apiKey,\n apiSecret: this.apiSecret,\n body: bodyToFetch,\n passphrase: this.passphrase,\n });\n\n const response = await fetch(this.endpoint, {\n body: bodyToFetch,\n headers: {\n accept: 'application/json',\n 'content-type': 'application/json',\n ...this.headers,\n ...signedHeaders,\n },\n method: 'POST',\n signal: controller.signal,\n });\n\n let body: GraphQLResponse<TData>;\n try {\n body = (await response.json()) as GraphQLResponse<TData>;\n } catch {\n this.logger?.log(\n 'error',\n 'Olympex GraphQL response was not valid JSON',\n redact({\n contentType: response.headers.get('content-type') ?? undefined,\n endpoint: this.endpoint,\n status: response.status,\n }),\n );\n throw new OlympexNetworkError('GraphQL response was not valid JSON', {\n contentType: response.headers.get('content-type') ?? undefined,\n status: response.status,\n statusText: response.statusText,\n });\n }\n\n if (!response.ok) {\n this.logger?.log(\n 'error',\n 'Olympex GraphQL request failed with HTTP status',\n redact({\n endpoint: this.endpoint,\n status: response.status,\n statusText: response.statusText,\n }),\n );\n throw new OlympexNetworkError(`GraphQL request failed with status ${response.status}`, {\n status: response.status,\n statusText: response.statusText,\n });\n }\n\n if (body?.errors?.length) {\n const firstError = body.errors[0];\n this.logger?.log(\n 'warn',\n 'Olympex GraphQL response contains errors',\n redact({\n endpoint: this.endpoint,\n errorCount: body.errors.length,\n extensions: firstError?.extensions,\n firstMessage: firstError?.message,\n }),\n );\n throw new OlympexGraphQLError(body.errors[0]?.message ?? 'GraphQL request failed', {\n errors: body.errors.map((error) => ({\n extensions: error.extensions,\n message: error.message,\n path: error.path,\n })),\n });\n }\n\n if (!body?.data) {\n throw new OlympexGraphQLError('GraphQL response did not include data');\n }\n\n return body.data;\n } catch (error) {\n if (error instanceof OlympexGraphQLError || error instanceof OlympexNetworkError) {\n throw error;\n }\n\n const errorName = error instanceof Error ? error.name : 'UnknownError';\n const errorMessage = error instanceof Error ? error.message : 'Unknown network error';\n this.logger?.log(\n 'error',\n 'Olympex GraphQL network failure',\n redact({\n endpoint: this.endpoint,\n errorMessage,\n errorName,\n }),\n );\n throw new OlympexNetworkError(errorMessage);\n } finally {\n clearTimeout(timeout);\n }\n }\n}\n","import { OlympexSdkError } from './base-error.js';\n\n/**\n * Thrown when SDK configuration or caller input fails local validation before a network call.\n *\n * @remarks Extends {@link OlympexSdkError}. Safe to catch by `instanceof` or `error.code`\n * (`OLYMPEX_CONFIG_ERROR`). Examples: empty `apiKey`, invalid `OLYMPEX_BACKEND_URL`, invalid\n * {@link FeeOptions.feeBps | feeBps}.\n *\n * @see docs/errors.md\n */\nexport class OlympexConfigError extends OlympexSdkError {\n constructor(message: string, metadata?: Record<string, unknown>) {\n super(message, 'OLYMPEX_CONFIG_ERROR', metadata);\n }\n}\n","import { isAddress } from 'ethers';\nimport { OlympexConfigError } from '../errors/config-error.js';\nimport type { FeeOptions } from '../types/public.js';\nimport { FEE_BPS_MAX, FEE_BPS_MIN } from './constants.js';\n\nexport function validateFeeOptions(fees: FeeOptions | undefined): void {\n if (!fees) {\n return;\n }\n\n if (fees.feeBps !== undefined) {\n if (!Number.isFinite(fees.feeBps) || fees.feeBps < FEE_BPS_MIN || fees.feeBps > FEE_BPS_MAX) {\n throw new OlympexConfigError(`feeBps must be between ${FEE_BPS_MIN} and ${FEE_BPS_MAX}`);\n }\n }\n\n if (fees.feeRecipient !== undefined && !isAddress(fees.feeRecipient)) {\n throw new OlympexConfigError('feeRecipient must be a valid Ethereum address');\n }\n}\n","export const FEE_BPS_MIN = 0;\nexport const FEE_BPS_MAX = 100;\n","import { OlympexConfigError } from '../errors/config-error.js';\n\nexport const DEFAULT_BACKEND_URL = 'https://back.olympex.io/graphql';\n\nconst BACKEND_URL_ENV = 'OLYMPEX_BACKEND_URL';\nconst GRAPHQL_PATH = '/graphql';\n\nfunction readBackendUrlEnv(): string | undefined {\n const raw = process.env[BACKEND_URL_ENV];\n if (raw === undefined) {\n return undefined;\n }\n\n const trimmed = raw.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction validateBackendUrl(url: string): URL {\n let parsed: URL;\n\n try {\n parsed = new URL(url);\n } catch {\n throw new OlympexConfigError(`Invalid ${BACKEND_URL_ENV}: ${url}`);\n }\n\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n throw new OlympexConfigError(`Invalid ${BACKEND_URL_ENV}: ${url}`);\n }\n\n if (parsed.search || parsed.hash) {\n throw new OlympexConfigError(`Invalid ${BACKEND_URL_ENV}: ${url}`);\n }\n\n const pathname = parsed.pathname.replace(/\\/$/, '') || '/';\n if (pathname !== '/' && pathname !== GRAPHQL_PATH) {\n throw new OlympexConfigError(`Invalid ${BACKEND_URL_ENV}: ${url}`);\n }\n\n return parsed;\n}\n\nexport function normalizeBase(baseUrl: string): string {\n return baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;\n}\n\nfunction toGraphqlEndpoint(url: string): string {\n const parsed = validateBackendUrl(url);\n const pathname = parsed.pathname.replace(/\\/$/, '') || '/';\n\n if (pathname === GRAPHQL_PATH) {\n return `${parsed.origin}${GRAPHQL_PATH}`;\n }\n\n return `${parsed.origin}${GRAPHQL_PATH}`;\n}\n\nexport function resolveBackendBaseUrl(): string {\n const configured = readBackendUrlEnv();\n const candidate = configured ?? DEFAULT_BACKEND_URL;\n return toGraphqlEndpoint(candidate);\n}\n\nexport function getGraphqlEndpoint(): string {\n return normalizeBase(resolveBackendBaseUrl());\n}\n\nexport function getRestBaseUrl(): string {\n const graphqlEndpoint = normalizeBase(resolveBackendBaseUrl());\n\n if (graphqlEndpoint.endsWith(GRAPHQL_PATH)) {\n return graphqlEndpoint.slice(0, -GRAPHQL_PATH.length);\n }\n\n return graphqlEndpoint;\n}\n","import { getRestBaseUrl } from '../config/backend-url.js';\nimport { OlympexConfigError } from '../errors/config-error.js';\nimport { OlympexNetworkError } from '../errors/network-error.js';\nimport type { CreateAccountOptions, CreateAccountResponse } from '../types/public.js';\n\nconst CREATE_ACCOUNT_PATH = '/api/v1/accounts';\n\ninterface CredentialsPayload {\n readonly apiKey: string;\n readonly secretKey: string;\n}\n\nfunction extractCredentials(payload: unknown): CreateAccountResponse | null {\n if (!payload || typeof payload !== 'object') {\n return null;\n }\n\n const directPayload = payload as Record<string, unknown>;\n const candidatePayloads: unknown[] = [directPayload];\n\n if (directPayload.data !== undefined) {\n candidatePayloads.push(directPayload.data);\n }\n\n for (const candidate of candidatePayloads) {\n if (!candidate || typeof candidate !== 'object') {\n continue;\n }\n\n const values = candidate as Partial<CredentialsPayload>;\n if (typeof values.apiKey === 'string' && typeof values.secretKey === 'string') {\n return {\n apiKey: values.apiKey,\n secretKey: values.secretKey,\n };\n }\n }\n\n return null;\n}\n\nfunction parseResponseMessage(payload: unknown): string | undefined {\n if (!payload || typeof payload !== 'object') {\n return undefined;\n }\n\n const maybeMessage = (payload as { message?: unknown }).message;\n return typeof maybeMessage === 'string' ? maybeMessage : undefined;\n}\n\n/**\n * Creates API credentials via public REST onboarding.\n *\n * @remarks Server-side only. Does not require {@link initialize}. Store returned `secretKey`\n * securely; it is shown once by the backend. Backend routing is resolved from\n * `OLYMPEX_BACKEND_URL` at call time.\n * @param options - {@link CreateAccountOptions} with onboarding fields.\n * @returns Fresh {@link CreateAccountResponse} credentials for subsequent {@link initialize}.\n * @throws {OlympexConfigError} When `name` or `password` is empty, or `OLYMPEX_BACKEND_URL` is invalid.\n * @throws {OlympexNetworkError} When REST onboarding fails or omits credentials.\n *\n * @see docs/authentication.md\n */\nexport async function createAccount({\n name,\n password,\n}: CreateAccountOptions): Promise<CreateAccountResponse> {\n const normalizedName = name.trim();\n const normalizedPassword = password.trim();\n\n if (!normalizedName) {\n throw new OlympexConfigError('name is required');\n }\n\n if (!normalizedPassword) {\n throw new OlympexConfigError('password is required');\n }\n\n const endpoint = `${getRestBaseUrl()}${CREATE_ACCOUNT_PATH}`;\n\n const response = await fetch(endpoint, {\n body: JSON.stringify({ name: normalizedName, password: normalizedPassword }),\n headers: {\n accept: 'application/json',\n 'content-type': 'application/json',\n },\n method: 'POST',\n });\n\n let responseBody: unknown;\n try {\n responseBody = await response.json();\n } catch {\n throw new OlympexNetworkError('createAccount response was not valid JSON', {\n contentType: response.headers.get('content-type') ?? undefined,\n endpoint,\n status: response.status,\n statusText: response.statusText,\n });\n }\n\n if (!response.ok) {\n throw new OlympexNetworkError(`createAccount request failed with status ${response.status}`, {\n endpoint,\n responseMessage: parseResponseMessage(responseBody),\n status: response.status,\n statusText: response.statusText,\n });\n }\n\n // TODO: tenemos el control tanto del código del SDK como del backend por lo que podemos\n // hacer que el SDK sepa como extraer las credenciales de la respuesta sin tener una\n // función tna verbosa de manera innecesaria\n const credentials = extractCredentials(responseBody);\n if (!credentials) {\n throw new OlympexNetworkError('createAccount response did not include credentials', {\n endpoint,\n status: response.status,\n });\n }\n\n return credentials;\n}\n","const VERSION = '0.0.0';\n\n/**\n * Returns the SDK package version string (standalone export).\n *\n * @remarks Prefer {@link OlympexClient.getVersion} on an initialized client for the same value.\n * @returns Semantic version of the installed SDK (e.g. `'0.0.0'`).\n *\n * @see docs/getting-started.md\n */\nexport function getVersion(): string {\n return VERSION;\n}\n","import { OlympexSdkError } from './base-error.js';\n\n/**\n * Thrown when the backend returns a domain-level `success: false` response.\n *\n * @remarks Extends {@link OlympexSdkError}. The HTTP request succeeded but the payload\n * indicates a business-rule or validation failure. Inspect `error.message` and `error.metadata`\n * for backend-provided context.\n *\n * @see docs/errors.md\n */\nexport class OlympexDomainError extends OlympexSdkError {\n constructor(message: string, metadata?: Record<string, unknown>) {\n super(message, 'OLYMPEX_DOMAIN_ERROR', metadata);\n }\n}\n","import { OlympexDomainError } from '../errors/domain-error.js';\n\ninterface DomainResponse {\n readonly code?: number;\n readonly message?: string;\n readonly success?: boolean;\n}\n\nexport function requireSuccessfulDomainResponse<TResponse extends DomainResponse>(\n response: TResponse | null | undefined,\n payloadName: string,\n): TResponse {\n if (response === null || response === undefined) {\n throw new OlympexDomainError(`Missing ${payloadName} response`);\n }\n\n if (response.success === false) {\n throw new OlympexDomainError(response.message ?? `${payloadName} failed`, {\n code: response.code,\n success: response.success,\n });\n }\n\n return response;\n}\n\nexport function requirePayload<TPayload>(\n payload: TPayload | null | undefined,\n payloadName: string,\n): TPayload {\n if (payload === null || payload === undefined) {\n throw new OlympexDomainError(`Missing ${payloadName} payload`);\n }\n\n return payload;\n}\n","import type { FeeOptions } from '../types/public.js';\n\nexport function resolveFeeOptions(\n callFees: FeeOptions | undefined,\n clientDefaultFees: FeeOptions | undefined,\n): FeeOptions | undefined {\n if (!callFees && !clientDefaultFees) {\n return undefined;\n }\n\n return {\n ...clientDefaultFees,\n ...callFees,\n };\n}\n","import type { FeeOptions } from '../types/public.js';\n\nexport function serializeFeeOptions(\n fees: FeeOptions | undefined,\n): Record<string, number | string> | undefined {\n if (!fees) {\n return undefined;\n }\n\n const serialized: Record<string, number | string> = {};\n\n if (fees.feeBps !== undefined) {\n serialized.feeBps = fees.feeBps;\n }\n\n if (fees.feeRecipient !== undefined) {\n serialized.feeRecipient = fees.feeRecipient;\n }\n\n return Object.keys(serialized).length > 0 ? serialized : undefined;\n}\n\nexport function mergeParamsWithFees<T extends Record<string, unknown>>(\n params: T,\n fees: FeeOptions | undefined,\n): T {\n const serialized = serializeFeeOptions(fees);\n\n if (!serialized) {\n return params;\n }\n\n return {\n ...params,\n ...serialized,\n };\n}\n","export const GET_ENABLED_CHAINS = /* GraphQL */ `\n query GetEnabledChains {\n getEnabledChains {\n chainIds\n }\n }\n`;\n\nexport const GET_QUOTE = /* GraphQL */ `\n query GetQuote($getQuoteParams: QuoteInput!) {\n getQuote(params: $getQuoteParams) {\n outAmount\n estimatedGas\n aggregatorId\n aggregatorOrder\n market {\n dexName\n swapAmount\n dexImageURL\n }\n routes {\n percentage\n subRoutes {\n from\n to\n dexes {\n name\n percentage\n }\n }\n }\n dataFeeTransaction {\n effectiveGasPrice\n nativePrice\n transactionFee\n transactionFeeInUSD\n tokenPrice\n transactionFeeInTokenIn\n valueToApprove\n }\n gasMultiplier\n }\n }\n`;\n\nexport const GET_QUOTE_SWAP = /* GraphQL */ `\n mutation GetQuoteSwap($getQuoteSwapParams: QuoteSwapInput!) {\n getQuoteSwap(params: $getQuoteSwapParams) {\n code\n message\n quoteSwap {\n data\n estimatedGas\n minOutAmount\n outAmount\n value\n approveTransaction\n }\n success\n }\n }\n`;\n\nexport const GET_CROSS_CHAIN_QUOTE = /* GraphQL */ `\n query GetCrossChainQuote($params: CrossChainQuoteInput!) {\n getCrossChainQuote(params: $params) {\n code\n message\n success\n crossChainQuote {\n aggregatorId\n estimatedGas\n estimatedTime\n estimateCostInUSD\n fromTokenAmount\n toTokenAmount\n minimumReceived\n bridgeInfo {\n icon\n displayName\n }\n middlewareRoute {\n chainFrom {\n fromAsset {\n address\n decimals\n symbol\n }\n toAsset {\n address\n decimals\n symbol\n }\n }\n chainTo {\n fromAsset {\n address\n decimals\n symbol\n }\n toAsset {\n address\n decimals\n symbol\n }\n }\n }\n }\n }\n }\n`;\n\nexport const GET_CROSS_CHAIN_SWAP = /* GraphQL */ `\n mutation GetCrossChainSwap($params: CrossChainSwapInput!) {\n getCrossChainSwap(params: $params) {\n code\n message\n success\n crossChainSwap {\n calldata\n value\n approveTransaction\n dexHash\n }\n }\n }\n`;\n\nexport const GET_CROSS_CHAIN_TX_STATUS = /* GraphQL */ `\n query GetCrossChainTxStatus($hash: String!, $chainId: String!, $dexHash: String!) {\n getCrossChainTxStatus(txHash: $hash, chainId: $chainId, dexHash: $dexHash) {\n message\n success\n txStatus {\n toChainId\n fromChainId\n detailStatus\n status\n }\n }\n }\n`;\n","import type { OlympexGraphQLClient } from '../client/graphql-client.js';\nimport { requirePayload, requireSuccessfulDomainResponse } from '../client/domain-response.js';\nimport { redact } from '../errors/redact.js';\nimport { resolveFeeOptions } from '../fees/defaults.js';\nimport { mergeParamsWithFees } from '../fees/serialize.js';\nimport { validateFeeOptions } from '../fees/validation.js';\nimport type { FeeOptions, OlympexLogger } from '../types/public.js';\nimport { GET_CROSS_CHAIN_QUOTE, GET_QUOTE } from '../graphql/operations.js';\nimport type {\n CrossChainQuote,\n QuoteRequest,\n QuoteResult,\n SingleChainQuote,\n} from '../types/public.js';\n\ninterface SingleChainQuoteData {\n readonly getQuote?: SingleChainQuote | null;\n}\n\ninterface CrossChainQuoteData {\n readonly getCrossChainQuote?: {\n readonly code: number;\n readonly message: string;\n readonly success: boolean;\n readonly crossChainQuote?: CrossChainQuote | null;\n } | null;\n}\n\nexport function createQuoteMethod(\n graphqlClient: OlympexGraphQLClient,\n defaultFees: FeeOptions | undefined,\n logger?: OlympexLogger,\n) {\n return async function quote(input: QuoteRequest): Promise<QuoteResult> {\n const fees = resolveFeeOptions(input.fees, defaultFees);\n validateFeeOptions(fees);\n\n if (input.mode === 'single-chain') {\n logger?.log(\n 'info',\n 'Olympex quote requested',\n redact({\n chainId: input.params.chainId,\n mode: 'single-chain',\n }),\n );\n\n const data = await graphqlClient.request<SingleChainQuoteData, Record<string, unknown>>(\n GET_QUOTE,\n {\n getQuoteParams: mergeParamsWithFees(input.params, fees),\n },\n );\n\n return {\n mode: 'single-chain',\n quote: requirePayload(data.getQuote, 'getQuote'),\n };\n }\n\n logger?.log(\n 'info',\n 'Olympex quote requested',\n redact({\n fromChainId: input.params.fromChainId,\n mode: 'cross-chain',\n toChainId: input.params.toChainId,\n }),\n );\n\n const data = await graphqlClient.request<CrossChainQuoteData, Record<string, unknown>>(\n GET_CROSS_CHAIN_QUOTE,\n {\n params: mergeParamsWithFees(input.params, fees),\n },\n );\n const response = requireSuccessfulDomainResponse(data.getCrossChainQuote, 'getCrossChainQuote');\n\n return {\n mode: 'cross-chain',\n quote: requirePayload(response.crossChainQuote, 'crossChainQuote'),\n };\n };\n}\n","import type { OlympexGraphQLClient } from '../client/graphql-client.js';\nimport { requirePayload } from '../client/domain-response.js';\nimport { GET_ENABLED_CHAINS } from '../graphql/operations.js';\n\ninterface EnabledChainsData {\n readonly getEnabledChains?: {\n readonly chainIds: readonly string[];\n } | null;\n}\n\nexport function createSupportChainMethod(graphqlClient: OlympexGraphQLClient) {\n return async function supportChain(chainId: string | number): Promise<boolean> {\n const data = await graphqlClient.request<EnabledChainsData, Record<string, never>>(\n GET_ENABLED_CHAINS,\n {},\n );\n const enabledChains = requirePayload(data.getEnabledChains, 'getEnabledChains');\n\n return enabledChains.chainIds.includes(String(chainId));\n };\n}\n","import type { OlympexGraphQLClient } from '../client/graphql-client.js';\nimport { requirePayload, requireSuccessfulDomainResponse } from '../client/domain-response.js';\nimport { redact } from '../errors/redact.js';\nimport { resolveFeeOptions } from '../fees/defaults.js';\nimport { mergeParamsWithFees } from '../fees/serialize.js';\nimport { validateFeeOptions } from '../fees/validation.js';\nimport type { FeeOptions, OlympexLogger } from '../types/public.js';\nimport { GET_CROSS_CHAIN_SWAP, GET_QUOTE_SWAP } from '../graphql/operations.js';\nimport type { CrossChainSwap, SingleChainSwap, SwapRequest, SwapResult } from '../types/public.js';\n\ninterface SingleChainSwapData {\n readonly getQuoteSwap?: {\n readonly code: number;\n readonly message: string;\n readonly quoteSwap?: SingleChainSwap | null;\n readonly success: boolean;\n } | null;\n}\n\ninterface CrossChainSwapData {\n readonly getCrossChainSwap?: {\n readonly code: number;\n readonly message: string;\n readonly success: boolean;\n readonly crossChainSwap?: CrossChainSwap | null;\n } | null;\n}\n\nexport function createSwapMethod(\n graphqlClient: OlympexGraphQLClient,\n defaultFees: FeeOptions | undefined,\n logger?: OlympexLogger,\n) {\n return async function swap(input: SwapRequest): Promise<SwapResult> {\n const fees = resolveFeeOptions(input.fees, defaultFees);\n validateFeeOptions(fees);\n\n if (input.mode === 'single-chain') {\n logger?.log(\n 'info',\n 'Olympex swap requested',\n redact({\n chainId: input.params.chainId,\n mode: 'single-chain',\n }),\n );\n\n const data = await graphqlClient.request<SingleChainSwapData, Record<string, unknown>>(\n GET_QUOTE_SWAP,\n {\n getQuoteSwapParams: mergeParamsWithFees(input.params, fees),\n },\n );\n const response = requireSuccessfulDomainResponse(data.getQuoteSwap, 'getQuoteSwap');\n\n return {\n mode: 'single-chain',\n swap: requirePayload(response.quoteSwap, 'quoteSwap'),\n };\n }\n\n logger?.log(\n 'info',\n 'Olympex swap requested',\n redact({\n fromChainId: input.params.fromChainId,\n mode: 'cross-chain',\n toChainId: input.params.toChainId,\n }),\n );\n\n const data = await graphqlClient.request<CrossChainSwapData, Record<string, unknown>>(\n GET_CROSS_CHAIN_SWAP,\n {\n params: mergeParamsWithFees(input.params, fees),\n },\n );\n const response = requireSuccessfulDomainResponse(data.getCrossChainSwap, 'getCrossChainSwap');\n\n return {\n mode: 'cross-chain',\n swap: requirePayload(response.crossChainSwap, 'crossChainSwap'),\n };\n };\n}\n","import type { OlympexGraphQLClient } from '../client/graphql-client.js';\nimport { requirePayload, requireSuccessfulDomainResponse } from '../client/domain-response.js';\nimport { GET_CROSS_CHAIN_TX_STATUS } from '../graphql/operations.js';\nimport type { TxStatus, TxStatusInput } from '../types/public.js';\n\ninterface TxStatusData {\n readonly getCrossChainTxStatus?: {\n readonly message: string;\n readonly success: boolean;\n readonly txStatus?: TxStatus | null;\n } | null;\n}\n\nexport function createTxStatusMethod(graphqlClient: OlympexGraphQLClient) {\n return async function txStatus(input: TxStatusInput): Promise<TxStatus> {\n const data = await graphqlClient.request<TxStatusData, TxStatusInput>(\n GET_CROSS_CHAIN_TX_STATUS,\n input,\n );\n const response = requireSuccessfulDomainResponse(\n data.getCrossChainTxStatus,\n 'getCrossChainTxStatus',\n );\n\n return requirePayload(response.txStatus, 'txStatus');\n };\n}\n","import { OlympexGraphQLClient } from '../client/graphql-client.js';\nimport { OlympexConfigError } from '../errors/config-error.js';\nimport { validateFeeOptions } from '../fees/validation.js';\nimport { createAccount } from '../methods/account.js';\nimport { getVersion } from '../methods/get-version.js';\nimport { createQuoteMethod } from '../methods/quote.js';\nimport { createSupportChainMethod } from '../methods/support-chain.js';\nimport { createSwapMethod } from '../methods/swap.js';\nimport { createTxStatusMethod } from '../methods/tx-status.js';\nimport type { InitializeOptions, OlympexClient } from '../types/public.js';\nimport { getGraphqlEndpoint } from './backend-url.js';\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\n/**\n * Creates an {@link OlympexClient} bound to an API key.\n *\n * @remarks Logging is silent-by-default unless {@link InitializeOptions.logger} is provided.\n * Fee defaults in `defaultFees` are validated locally then forwarded to the backend.\n * Backend routing is resolved from `OLYMPEX_BACKEND_URL` at call time.\n * @param options - {@link InitializeOptions} configuration.\n * @returns Configured {@link OlympexClient} ready for quote, swap, and status calls.\n * @throws {OlympexConfigError} When auth credentials are empty, `OLYMPEX_BACKEND_URL` is invalid, or `defaultFees` fail validation.\n *\n * @example Minimal bootstrap\n * ```ts\n * import { initialize } from '@olympex-io/olympex-sdk';\n *\n * const client = initialize({\n * apiKey: process.env.OLYMPEX_API_KEY!,\n * apiSecret: process.env.OLYMPEX_API_SECRET!,\n * passphrase: process.env.OLYMPEX_PASSPHRASE!,\n * });\n * ```\n *\n * @example With opt-in console logging\n * ```ts\n * import { initialize, createConsoleLogger } from '@olympex-io/olympex-sdk';\n *\n * const client = initialize({\n * apiKey: process.env.OLYMPEX_API_KEY!,\n * apiSecret: process.env.OLYMPEX_API_SECRET!,\n * passphrase: process.env.OLYMPEX_PASSPHRASE!,\n * logger: createConsoleLogger({ minLevel: 'debug' }),\n * });\n * ```\n *\n * @see docs/getting-started.md\n */\nexport function initialize(options: InitializeOptions): OlympexClient {\n const apiKey = options.apiKey.trim();\n const apiSecret = options.apiSecret.trim();\n const passphrase = options.passphrase.trim();\n\n if (!apiKey) {\n throw new OlympexConfigError('apiKey is required');\n }\n\n if (!apiSecret) {\n throw new OlympexConfigError('apiSecret is required');\n }\n\n if (!passphrase) {\n throw new OlympexConfigError('passphrase is required');\n }\n\n validateFeeOptions(options.defaultFees);\n\n const graphqlClient = new OlympexGraphQLClient({\n apiKey,\n apiSecret,\n endpoint: getGraphqlEndpoint(),\n headers: options.headers,\n logger: options.logger,\n passphrase,\n timeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n });\n\n return {\n apiKey,\n createAccount,\n getVersion,\n quote: createQuoteMethod(graphqlClient, options.defaultFees, options.logger),\n supportChain: createSupportChainMethod(graphqlClient),\n swap: createSwapMethod(graphqlClient, options.defaultFees, options.logger),\n txStatus: createTxStatusMethod(graphqlClient),\n };\n}\n","import type { LogLevel, OlympexLogger } from '../types/public.js';\n\nconst LEVEL_RANK: Record<LogLevel, number> = {\n debug: 10,\n info: 20,\n warn: 30,\n error: 40,\n};\n\n/**\n * Options for {@link createConsoleLogger}.\n *\n * @property minLevel - Minimum {@link LogLevel} to emit; defaults to `'info'`.\n */\nexport interface CreateConsoleLoggerOptions {\n readonly minLevel?: LogLevel;\n}\n\n/**\n * Returns a safe {@link OlympexLogger} that writes to `console` at the configured minimum level.\n *\n * @remarks Implementations never throw. Messages below `minLevel` are filtered out.\n * @param options - Optional {@link CreateConsoleLoggerOptions}; `minLevel` defaults to `'info'`.\n * @returns Frozen {@link OlympexLogger} suitable for {@link InitializeOptions.logger}.\n *\n * @example\n * ```ts\n * import { createConsoleLogger } from '@olympex-io/olympex-sdk';\n *\n * const logger = createConsoleLogger({ minLevel: 'warn' });\n * logger.log('info', 'filtered out');\n * logger.log('warn', 'visible', { requestId: 'abc' });\n * ```\n *\n * @see docs/logging.md\n */\nexport function createConsoleLogger(options: CreateConsoleLoggerOptions = {}): OlympexLogger {\n const minRank = LEVEL_RANK[options.minLevel ?? 'info'];\n\n const target = (level: LogLevel): ((...args: unknown[]) => void) => {\n const method = globalThis.console[level];\n return typeof method === 'function'\n ? method.bind(globalThis.console)\n : globalThis.console.log.bind(globalThis.console);\n };\n\n return Object.freeze({\n log(level: LogLevel, message: string, metadata?: Record<string, unknown>) {\n if (LEVEL_RANK[level] < minRank) {\n return;\n }\n\n try {\n const write = target(level);\n if (metadata === undefined) {\n write(message);\n } else {\n write(message, metadata);\n }\n } catch {\n /* logging must never throw */\n }\n },\n });\n}\n","import { OlympexSdkError } from './base-error.js';\n\n/**\n * Thrown when a public method is not yet backed by a live backend contract.\n *\n * @remarks Extends {@link OlympexSdkError}. Use when exposing explicitly unsupported\n * temporary paths while backend contracts are still in progress.\n *\n * @see docs/errors.md\n */\nexport class OlympexNotImplementedError extends OlympexSdkError {\n constructor(message: string, metadata?: Record<string, unknown>) {\n super(message, 'OLYMPEX_NOT_IMPLEMENTED', metadata);\n }\n}\n"],"mappings":"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","names":["data","data","response"]}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Authentication
|
|
2
|
+
|
|
3
|
+
## Overview — server-side only
|
|
4
|
+
|
|
5
|
+
The Olympex SDK is a **server-side only** integration library. Run it in Node.js (22.15.0+)
|
|
6
|
+
environments you control: your backend, BFF, or serverless functions.
|
|
7
|
+
|
|
8
|
+
Three credentials are required for signed GraphQL traffic:
|
|
9
|
+
|
|
10
|
+
| Credential | Source | Used as |
|
|
11
|
+
| ------------ | ----------------------------- | --------------------- |
|
|
12
|
+
| `apiKey` | `createAccount` response | `x-api-key-id` header |
|
|
13
|
+
| `apiSecret` | `createAccount` → `secretKey` | HMAC signing secret |
|
|
14
|
+
| `passphrase` | `createAccount` → `password` | `x-passphrase` header |
|
|
15
|
+
|
|
16
|
+
Never embed `apiSecret` or `passphrase` in browser bundles, mobile apps, or public repos.
|
|
17
|
+
The SDK does not provide a browser-safe mode.
|
|
18
|
+
|
|
19
|
+
> **Future (out of scope):** A v2 browser/proxy mode may exist later. Web applications
|
|
20
|
+
> must use a server-side proxy today (see [Server-side proxy pattern](#server-side-proxy-pattern)).
|
|
21
|
+
|
|
22
|
+
## Credential lifecycle
|
|
23
|
+
|
|
24
|
+
### 1. Bootstrap — `createAccount` (REST, once)
|
|
25
|
+
|
|
26
|
+
Onboarding is a **public REST call**. It does not use signed headers.
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
POST /api/v1/accounts
|
|
30
|
+
Content-Type: application/json
|
|
31
|
+
|
|
32
|
+
{ "name": "My App", "password": "<choose-a-strong-passphrase>" }
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Success response (shape may be wrapped in `data`):
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"apiKey": "550e8400-e29b-41d4-a716-446655440000",
|
|
40
|
+
"secretKey": "base64url-encoded-secret-43-chars"
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
- `secretKey` is returned **once**. Store it immediately; it cannot be retrieved later.
|
|
45
|
+
- The `password` you send becomes your signing **passphrase** (hashed server-side).
|
|
46
|
+
|
|
47
|
+
SDK usage (no `initialize` required):
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { createAccount, initialize } from '@olympex-io/olympex-sdk';
|
|
51
|
+
|
|
52
|
+
const { apiKey, secretKey } = await createAccount({
|
|
53
|
+
name: 'My App',
|
|
54
|
+
password: process.env.OLYMPEX_ACCOUNT_PASSWORD!,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Persist in your secret store — never commit to git
|
|
58
|
+
// apiKey → OLYMPEX_API_KEY
|
|
59
|
+
// secretKey → OLYMPEX_API_SECRET
|
|
60
|
+
// password → OLYMPEX_PASSPHRASE
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 2. Store secrets securely
|
|
64
|
+
|
|
65
|
+
Use environment variables or a secret manager (AWS Secrets Manager, Vault, etc.).
|
|
66
|
+
Map credentials as follows when calling `initialize`:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
const client = initialize({
|
|
70
|
+
apiKey: process.env.OLYMPEX_API_KEY!,
|
|
71
|
+
apiSecret: process.env.OLYMPEX_API_SECRET!, // secretKey from createAccount
|
|
72
|
+
passphrase: process.env.OLYMPEX_PASSPHRASE!, // password from createAccount
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Set `OLYMPEX_BACKEND_URL` per deployment (see [Getting started — Backend URL](./getting-started.md#backend-url)).
|
|
77
|
+
|
|
78
|
+
### 3. Signed GraphQL — every protected call
|
|
79
|
+
|
|
80
|
+
All SDK methods that hit GraphQL (`quote`, `swap`, `txStatus`, `supportChain`, etc.)
|
|
81
|
+
automatically attach signed headers. You do not sign manually when using the SDK.
|
|
82
|
+
|
|
83
|
+
The backend resolves your **integrator channel** and fee policy from this signed auth context — the SDK does not send or infer channel identifiers.
|
|
84
|
+
|
|
85
|
+
## Signed request algorithm
|
|
86
|
+
|
|
87
|
+
The SDK applies the following signing contract to every GraphQL request. Steps:
|
|
88
|
+
|
|
89
|
+
1. **Serialize body** — exact JSON string sent as the HTTP body (e.g. GraphQL payload).
|
|
90
|
+
2. **bodyHash** — `SHA256(body)` digest encoded as **base64url** (no padding).
|
|
91
|
+
3. **timestamp** — Unix seconds, string (e.g. `"1718812800"`).
|
|
92
|
+
4. **nonce** — 24 hex chars (`crypto.randomBytes(12).toString('hex')`).
|
|
93
|
+
5. **message** — `` `${timestamp}\n${nonce}\n${bodyHash}` `` (literal newline separators).
|
|
94
|
+
6. **x-value-info** — `Buffer.from(message).toString('base64')`.
|
|
95
|
+
7. **x-signature** — `HMAC-SHA256(message, apiSecret)` as lowercase hex.
|
|
96
|
+
|
|
97
|
+
### Pseudocode example
|
|
98
|
+
|
|
99
|
+
```text
|
|
100
|
+
body = '{"query":"...","variables":{...}}'
|
|
101
|
+
bodyHash = base64url( SHA256(body) )
|
|
102
|
+
timestamp = "1718812800"
|
|
103
|
+
nonce = "a1b2c3d4e5f6789012345678"
|
|
104
|
+
message = timestamp + "\n" + nonce + "\n" + bodyHash
|
|
105
|
+
x-value-info = base64( message )
|
|
106
|
+
x-signature = hex( HMAC_SHA256(key=apiSecret, data=message) )
|
|
107
|
+
x-api-key-id = apiKey
|
|
108
|
+
x-passphrase = passphrase
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The Olympex API validates `x-value-info`, checks the timestamp window (±300 s),
|
|
112
|
+
verifies the passphrase, and confirms `x-signature` with timing-safe comparison.
|
|
113
|
+
|
|
114
|
+
## Headers reference
|
|
115
|
+
|
|
116
|
+
| Header | Required | Description |
|
|
117
|
+
| -------------- | -------- | ----------------------------------------------- |
|
|
118
|
+
| `x-api-key-id` | Yes | API key UUID from `createAccount` |
|
|
119
|
+
| `x-value-info` | Yes | Base64 of `timestamp\nnonce\nbodyHash` |
|
|
120
|
+
| `x-passphrase` | Yes | Passphrase chosen at account creation |
|
|
121
|
+
| `x-signature` | Yes | HMAC-SHA256 hex of `timestamp\nnonce\nbodyHash` |
|
|
122
|
+
| `content-type` | Yes | `application/json` for GraphQL POST bodies |
|
|
123
|
+
|
|
124
|
+
## Error handling
|
|
125
|
+
|
|
126
|
+
| HTTP status | Stage | Typical cause |
|
|
127
|
+
| ----------- | ------------------ | --------------------------------------------------------------------------------------------- |
|
|
128
|
+
| **401** | Authentication | Missing/invalid headers, bad signature, wrong passphrase, expired timestamp, inactive account |
|
|
129
|
+
| **403** | Request validation | Body hash mismatch — request body was altered after signing |
|
|
130
|
+
|
|
131
|
+
Authentication failures return **401 Unauthorized** before GraphQL executes.
|
|
132
|
+
|
|
133
|
+
Body hash validation runs after authentication. If the signed `bodyHash` does not match
|
|
134
|
+
`SHA256(actual body)`, the API returns **403 Forbidden** with:
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
{
|
|
138
|
+
"error": "Forbidden",
|
|
139
|
+
"message": "Invalid body hash",
|
|
140
|
+
"statusCode": 403
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
SDK callers typically see these as `OlympexNetworkError` with the HTTP status attached.
|
|
145
|
+
|
|
146
|
+
## Server-side proxy pattern
|
|
147
|
+
|
|
148
|
+
Web apps must **not** call Olympex with API secrets from the browser.
|
|
149
|
+
|
|
150
|
+
```mermaid
|
|
151
|
+
flowchart LR
|
|
152
|
+
Browser["Browser<br/>(no secrets)"] --> BFF["Your BFF/API<br/>(signs requests)"]
|
|
153
|
+
BFF --> SDK["Olympex SDK<br/>+ GraphQL API"]
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Your server-side proxy owns credential storage, request signing, and any user/session auth.
|
|
157
|
+
Olympex does not ship a proxy layer for browser clients.
|
|
158
|
+
|
|
159
|
+
## Security checklist
|
|
160
|
+
|
|
161
|
+
- [ ] Never commit `apiSecret`, `passphrase`, or `secretKey` to version control
|
|
162
|
+
- [ ] Load credentials from env vars or a secret manager at runtime
|
|
163
|
+
- [ ] Restrict server/BFF network access; use IP allowlists when the API supports them
|
|
164
|
+
- [ ] Rotate compromised credentials with Olympex ops (new account / key rotation policy)
|
|
165
|
+
- [ ] Log and alert on repeated 401/403 responses
|
|
166
|
+
- [ ] Run `createAccount` only from trusted server automation, not from client code
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# Contributing JSDoc to the public SDK surface
|
|
2
|
+
|
|
3
|
+
JSDoc on symbols exported from `src/index.ts` flows into `dist/index.d.ts` for IDE hover text and published types. CI enforces compliance automatically — this guide is the contributor-facing source of truth for that contract.
|
|
4
|
+
|
|
5
|
+
## Neutral wording
|
|
6
|
+
|
|
7
|
+
Public JSDoc and `docs/` must use **neutral technical language**. Describe contracts and behavior without assuming who consumes the SDK.
|
|
8
|
+
|
|
9
|
+
## Enforcement layers
|
|
10
|
+
|
|
11
|
+
| Layer | Command | What it checks |
|
|
12
|
+
| ---------------- | -------------------------------------------------- | -------------------------------------------------------------------------------------------- |
|
|
13
|
+
| ESLint | `yarn lint` | Baseline tags on public implementation files (`eslint-plugin-jsdoc`) |
|
|
14
|
+
| Export validator | `yarn lint:jsdoc` | Tier rules, manifest membership, `@example` quotas, every `index.ts` export |
|
|
15
|
+
| Snapshot | `yarn test tests/jsdoc/dts-jsdoc-snapshot.test.ts` | Full JSDoc text for nine Tier-1 keys in `dist/index.d.ts` (runs `yarn build` in `beforeAll`) |
|
|
16
|
+
|
|
17
|
+
Run locally before opening a PR:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
yarn typecheck
|
|
21
|
+
yarn lint
|
|
22
|
+
yarn lint:jsdoc
|
|
23
|
+
yarn test
|
|
24
|
+
yarn build
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
CI order: `typecheck` → `lint` → `lint:jsdoc` → `test` → `coverage` → `build`.
|
|
28
|
+
|
|
29
|
+
## Where to write JSDoc
|
|
30
|
+
|
|
31
|
+
- **Do** attach comments to the **implementation site** (`src/types/public.ts`, `src/config/initialize.ts`, `src/errors/*.ts`, etc.).
|
|
32
|
+
- **Do not** add JSDoc on `src/index.ts` re-exports (barrel only).
|
|
33
|
+
- **Do not** add JSDoc under internal paths: `src/client/`, `src/fees/`, `src/graphql/operations.ts`, `src/errors/redact.ts`, or method factories (`src/methods/quote.ts`, etc.).
|
|
34
|
+
|
|
35
|
+
## Symbol tiers
|
|
36
|
+
|
|
37
|
+
Tiers are defined in `scripts/public-jsdoc-tier-manifest.json`.
|
|
38
|
+
|
|
39
|
+
| Tier | Examples | Required tags |
|
|
40
|
+
| ----- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
41
|
+
| **1** | `initialize`, `OlympexClient` methods, `FeeOptions`, `InitializeOptions`, error classes | Summary, `@remarks`, `@param` / `@returns` (callables), `@throws` (when listed in manifest), `@see`, `{@link}`, `@example` on entry points |
|
|
42
|
+
| **2** | Request/result unions, `createConsoleLogger`, `getVersion`, `TxStatusInput`, constants | Summary, `@see`, `{@link}`, `@property` on interface fields (or inline property docs) |
|
|
43
|
+
| **3** | Nested response types (`QuoteRoute`, `QuoteMarket`, …) | One-line summary only — no field-level `@property` |
|
|
44
|
+
|
|
45
|
+
### Unknown exports
|
|
46
|
+
|
|
47
|
+
If a symbol is exported from `src/index.ts` but **missing from the manifest**, `yarn lint:jsdoc` fails twice:
|
|
48
|
+
|
|
49
|
+
1. `Export 'SymbolName' missing from manifest — add tier entry`
|
|
50
|
+
2. Tier-2 tag violations (default tier until listed)
|
|
51
|
+
|
|
52
|
+
Add an explicit tier entry — do not rely on the default.
|
|
53
|
+
|
|
54
|
+
## Checklist: new public API (e.g. `limitOrder`)
|
|
55
|
+
|
|
56
|
+
### 1. Implement and export
|
|
57
|
+
|
|
58
|
+
- Add the method to `OlympexClient` in `src/types/public.ts` with JSDoc on the interface method.
|
|
59
|
+
- Add related public types (inputs, results) in the same file or appropriate public module.
|
|
60
|
+
- Wire the runtime implementation (e.g. `src/methods/limit-order.ts` — **no JSDoc there**).
|
|
61
|
+
- Return the method from `initialize()` in `src/config/initialize.ts`.
|
|
62
|
+
- Re-export from `src/index.ts` if needed.
|
|
63
|
+
|
|
64
|
+
### 2. Update the tier manifest
|
|
65
|
+
|
|
66
|
+
Edit `scripts/public-jsdoc-tier-manifest.json`:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"tiers": {
|
|
71
|
+
"1": ["OlympexClient.limitOrder", "..."]
|
|
72
|
+
},
|
|
73
|
+
"throwsRequired": ["OlympexClient.limitOrder"],
|
|
74
|
+
"exampleQuotas": {
|
|
75
|
+
"perSymbolMinimums": { "limitOrder": 1 },
|
|
76
|
+
"entryPointSymbols": ["OlympexClient.limitOrder"]
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Only add `perSymbolMinimums` / `entryPointSymbols` when the symbol is an integration entry point with `@example`. Nested DTO types belong in tier **2** or **3**, not tier 1.
|
|
82
|
+
|
|
83
|
+
### 3. Write JSDoc (Tier 1 method template)
|
|
84
|
+
|
|
85
|
+
Copy style from `OlympexClient.quote` or `initialize`:
|
|
86
|
+
|
|
87
|
+
````ts
|
|
88
|
+
/**
|
|
89
|
+
* Short one-line summary.
|
|
90
|
+
*
|
|
91
|
+
* @remarks SDK forwards params unchanged; does not compute routes or fees locally.
|
|
92
|
+
* @param input - {@link LimitOrderInput} order parameters.
|
|
93
|
+
* @returns {@link LimitOrderResult} normalized backend response.
|
|
94
|
+
* @throws {OlympexConfigError} Local validation failure.
|
|
95
|
+
* @throws {OlympexDomainError} Backend domain error.
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```ts
|
|
99
|
+
* const result = await client.limitOrder({ ... });
|
|
100
|
+
* ```
|
|
101
|
+
*
|
|
102
|
+
* @see docs/methods/limit-order.md
|
|
103
|
+
*/
|
|
104
|
+
limitOrder(input: LimitOrderInput): Promise<LimitOrderResult>;
|
|
105
|
+
````
|
|
106
|
+
|
|
107
|
+
Tier-2 interfaces need `@see`, at least one `{@link}`, and `@property` for non-obvious fields (`mode`, `feeBps`, etc.).
|
|
108
|
+
|
|
109
|
+
### 4. `@see` targets
|
|
110
|
+
|
|
111
|
+
Prefer existing repo docs:
|
|
112
|
+
|
|
113
|
+
| Topic | `@see` |
|
|
114
|
+
| -------------- | ------------------------------------------------- |
|
|
115
|
+
| Bootstrap | `docs/getting-started.md` |
|
|
116
|
+
| Fees | `docs/fees.md` |
|
|
117
|
+
| Auth / account | `docs/authentication.md` |
|
|
118
|
+
| Logging | `docs/logging.md` |
|
|
119
|
+
| Methods | `docs/methods/<method>.md` (planned paths are OK) |
|
|
120
|
+
| Errors | `docs/errors.md` (planned) |
|
|
121
|
+
|
|
122
|
+
Use short `@example` blocks in JSDoc; long guides stay in markdown.
|
|
123
|
+
|
|
124
|
+
### 5. Verify
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
yarn lint:jsdoc # fix until exit 0
|
|
128
|
+
yarn lint
|
|
129
|
+
yarn typecheck
|
|
130
|
+
yarn test
|
|
131
|
+
yarn build
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Fix validator output first — messages include `file:line symbol [tier N] message`.
|
|
135
|
+
|
|
136
|
+
## Snapshot regression (nine Tier-1 keys)
|
|
137
|
+
|
|
138
|
+
Snapshots cover: `initialize`, `quote`, `swap`, and the six exported error classes.
|
|
139
|
+
|
|
140
|
+
- The snapshot test **always rebuilds** (`yarn build` in `beforeAll`) and reads `dist/index.d.ts`, not `src/` directly.
|
|
141
|
+
- Changing JSDoc wording on a snapshotted symbol without updating the snapshot fails:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
yarn test tests/jsdoc/dts-jsdoc-snapshot.test.ts
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
- Intentional JSDoc updates:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
yarn test tests/jsdoc/dts-jsdoc-snapshot.test.ts -u
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Commit the updated `tests/jsdoc/__snapshots__/dts-jsdoc-snapshot.test.ts.snap` in the same PR.
|
|
154
|
+
|
|
155
|
+
New methods like `limitOrder` are **not** snapshotted unless you extend `SNAPSHOT_SYMBOL_KEYS` in `tests/jsdoc/extract-dts-jsdoc.ts` (optional, for critical entry points only).
|
|
156
|
+
|
|
157
|
+
## `@example` quotas
|
|
158
|
+
|
|
159
|
+
The validator enforces:
|
|
160
|
+
|
|
161
|
+
- **Global:** ≥ 12 `@example` tags across `entryPointSymbols` in the manifest.
|
|
162
|
+
- **Per symbol:** at least one `@example` each on `initialize`, `quote`, `swap`, and `createConsoleLogger` (aliases map `quote` / `swap` → `OlympexClient.*`).
|
|
163
|
+
|
|
164
|
+
Both gates fail independently.
|
|
165
|
+
|
|
166
|
+
## Quick reference
|
|
167
|
+
|
|
168
|
+
| Task | File / command |
|
|
169
|
+
| --------------- | ---------------------------------------------------------------- |
|
|
170
|
+
| Tier list | `scripts/public-jsdoc-tier-manifest.json` |
|
|
171
|
+
| Validator logic | `scripts/validate-export-jsdoc.ts`, `scripts/lib/jsdoc-utils.ts` |
|
|
172
|
+
| Lint script | `yarn lint:jsdoc` |
|
|
173
|
+
| Snapshots | `tests/jsdoc/dts-jsdoc-snapshot.test.ts` |
|
|
174
|
+
| This guide | `docs/contributing-jsdoc.md` |
|
|
175
|
+
|
|
176
|
+
## Common mistakes
|
|
177
|
+
|
|
178
|
+
| Symptom | Cause | Fix |
|
|
179
|
+
| ----------------------------------- | -------------------------------------------------- | ---------------------------------------------------------------------------------- |
|
|
180
|
+
| `missing from manifest` | New export not in JSON | Add tier entry |
|
|
181
|
+
| `missing required @see` / `{@link}` | Tier 1–2 tags incomplete | Add tags per tier table |
|
|
182
|
+
| `missing required @property` | Tier-2 interface without field docs | Add `@property` or inline member JSDoc |
|
|
183
|
+
| Snapshot passes but JSDoc changed | Stale `dist/` (older test behavior) | Snapshot test now rebuilds; run `yarn test tests/jsdoc/dts-jsdoc-snapshot.test.ts` |
|
|
184
|
+
| `yarn build` fails DTS | Interface method not implemented in `initialize()` | Wire runtime before expecting green snapshots |
|
package/docs/errors.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Error handling
|
|
2
|
+
|
|
3
|
+
The SDK throws typed subclasses of `OlympexSdkError`. Catch by `instanceof` or compare `error.code` for stable handling across releases.
|
|
4
|
+
|
|
5
|
+
## Error class matrix
|
|
6
|
+
|
|
7
|
+
| Class | Code | When thrown | Network call? | Typical action |
|
|
8
|
+
| ---------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | ----------------------------------------------------------------------- |
|
|
9
|
+
| `OlympexConfigError` | `OLYMPEX_CONFIG_ERROR` | Invalid `initialize` options, invalid `OLYMPEX_BACKEND_URL`, invalid local input (e.g. `feeBps` > 100, malformed address) | **No** — fail-fast before fetch | Fix caller input or env configuration |
|
|
10
|
+
| `OlympexDomainError` | `OLYMPEX_DOMAIN_ERROR` | HTTP 200 with backend payload `success: false` (business rule rejection) | Yes (completed) | Inspect `message` and `metadata`; adjust request or contact Olympex ops |
|
|
11
|
+
| `OlympexGraphQLError` | `OLYMPEX_GRAPHQL_ERROR` | HTTP 200 with GraphQL `errors[]` (schema/execution failure) | Yes (completed) | Inspect GraphQL error details; often input or backend state issue |
|
|
12
|
+
| `OlympexNetworkError` | `OLYMPEX_NETWORK_ERROR` | Timeouts, fetch failures, non-2xx HTTP, invalid JSON body, auth HTTP failures (401/403) | Attempted | Retry with backoff; check connectivity, credentials, and backend status |
|
|
13
|
+
| `OlympexNotImplementedError` | `OLYMPEX_NOT_IMPLEMENTED` | Public method not yet backed by live backend contract | Varies | Upgrade SDK or wait for backend rollout; see changelog |
|
|
14
|
+
| `OlympexSdkError` | `OLYMPEX_SDK_ERROR` | Base class for unexpected SDK failures | Varies | Log `code` + `metadata`; treat as SDK bug if reproducible |
|
|
15
|
+
|
|
16
|
+
## Failure boundaries
|
|
17
|
+
|
|
18
|
+
### SDK-local validation (`OlympexConfigError`)
|
|
19
|
+
|
|
20
|
+
Thrown when the SDK can detect invalid input **without** backend state:
|
|
21
|
+
|
|
22
|
+
- Empty `apiKey`, `apiSecret`, or `passphrase` at `initialize`
|
|
23
|
+
- `feeBps` outside 0–100
|
|
24
|
+
- Malformed Ethereum address in `feeRecipient`
|
|
25
|
+
- Invalid backend URL resolution
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { OlympexConfigError } from '@olympex-io/olympex-sdk';
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
await client.quote({
|
|
32
|
+
mode: 'single-chain',
|
|
33
|
+
params: {
|
|
34
|
+
/* ... */
|
|
35
|
+
},
|
|
36
|
+
fees: { feeBps: 150 },
|
|
37
|
+
});
|
|
38
|
+
} catch (error) {
|
|
39
|
+
if (error instanceof OlympexConfigError) {
|
|
40
|
+
// Fix integrator input — no request reached Olympex
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Backend/domain failures (`OlympexDomainError`, `OlympexGraphQLError`)
|
|
46
|
+
|
|
47
|
+
Thrown after a successful HTTP transport when the backend reports a domain or GraphQL failure. The SDK does not retry or reinterpret these responses.
|
|
48
|
+
|
|
49
|
+
### Transport/auth failures (`OlympexNetworkError`)
|
|
50
|
+
|
|
51
|
+
Includes authentication failures returned as HTTP **401** (invalid signature, passphrase, or timestamp window) and **403** (body hash mismatch). See [`authentication.md`](./authentication.md#error-handling).
|
|
52
|
+
|
|
53
|
+
## Metadata and redaction
|
|
54
|
+
|
|
55
|
+
All errors may include `metadata` with diagnostic context. The SDK redacts sensitive keys (secrets, tokens) in log paths; treat raw error objects as potentially sensitive in production logs.
|
|
56
|
+
|
|
57
|
+
## Related docs
|
|
58
|
+
|
|
59
|
+
- [`methods/quote.md`](./methods/quote.md) — quote-specific inputs that trigger config errors
|
|
60
|
+
- [`methods/swap.md`](./methods/swap.md) — swap-specific inputs
|
|
61
|
+
- [`fees.md`](./fees.md) — fee validation rules
|
package/docs/fees.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Fee options
|
|
2
|
+
|
|
3
|
+
Optional fee fields on `quote` and `swap` declare **integrator margin** — where your share of the trade fee should accrue. The SDK validates shape and bounds locally, then forwards the fields unchanged. It never computes fee amounts, routes, or payout splits.
|
|
4
|
+
|
|
5
|
+
## Responsibility model
|
|
6
|
+
|
|
7
|
+
| Layer | Owns |
|
|
8
|
+
| ----------------------- | ------------------------------------------------------------------------------------------------ |
|
|
9
|
+
| **Integrator (you)** | Supplying `feeBps` (0–100) and `feeRecipient` when you want margin on a quote/swap |
|
|
10
|
+
| **SDK** | Local validation (range, address format) and GraphQL serialization only |
|
|
11
|
+
| **Backend** | Resolving integrator channel from signed auth (`x-api-key-id`, signature headers) |
|
|
12
|
+
| **Backend + contracts** | Applying `platformFeeIntegrator` (integrator-channel protocol fee) and partner commercial policy |
|
|
13
|
+
|
|
14
|
+
The SDK does **not** identify whether the caller is an external integrator or Olympex's own frontend. Channel classification and protocol fees are backend/contracts-owned from the signed partner auth context.
|
|
15
|
+
|
|
16
|
+
## Fee fields
|
|
17
|
+
|
|
18
|
+
| Field | Type | SDK behavior |
|
|
19
|
+
| -------------- | ---------------- | ----------------------------------------------------------- |
|
|
20
|
+
| `feeBps` | `number` (0–100) | Validated locally when present; forwarded to GraphQL params |
|
|
21
|
+
| `feeRecipient` | Ethereum address | Format-validated when present; forwarded to GraphQL params |
|
|
22
|
+
|
|
23
|
+
When a field is omitted, the SDK does not send it. The backend decides whether fee fields apply for your authenticated account and channel.
|
|
24
|
+
|
|
25
|
+
Set defaults once via `initialize({ defaultFees })`, or override per call with `fees`.
|
|
26
|
+
|
|
27
|
+
## Example
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { initialize } from '@olympex-io/olympex-sdk';
|
|
31
|
+
|
|
32
|
+
const client = initialize({
|
|
33
|
+
apiKey: process.env.OLYMPEX_API_KEY!,
|
|
34
|
+
apiSecret: process.env.OLYMPEX_API_SECRET!,
|
|
35
|
+
passphrase: process.env.OLYMPEX_PASSPHRASE!,
|
|
36
|
+
defaultFees: {
|
|
37
|
+
feeBps: 15,
|
|
38
|
+
feeRecipient: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
await client.quote({
|
|
43
|
+
mode: 'single-chain',
|
|
44
|
+
params: {
|
|
45
|
+
chainId: 1,
|
|
46
|
+
inTokenAddress: '0x...',
|
|
47
|
+
outTokenAddress: '0x...',
|
|
48
|
+
amount: '1',
|
|
49
|
+
slippage: '1',
|
|
50
|
+
gasPrice: '30',
|
|
51
|
+
},
|
|
52
|
+
fees: { feeBps: 20, feeRecipient: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb' },
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## What the SDK does not do
|
|
57
|
+
|
|
58
|
+
- Compute quotes, routes, or fee breakdowns
|
|
59
|
+
- Expose or accept `platformFeeIntegrator` as consumer input
|
|
60
|
+
- Branch on caller channel or apply Olympex protocol fee tiers locally
|
|
61
|
+
|
|
62
|
+
Protocol fees configured by Olympex for your integrator channel are applied by the backend from your authenticated account — SDK consumers do not set them.
|
|
63
|
+
|
|
64
|
+
See also [`authentication.md`](./authentication.md) for how channel context is derived from signed requests.
|