Skip to content

feat: add public swap API server - #11586

Merged
0xApotheosis merged 46 commits into
developfrom
feat/public-swap-api
Jan 20, 2026
Merged

feat: add public swap API server#11586
0xApotheosis merged 46 commits into
developfrom
feat/public-swap-api

Conversation

@0xApotheosis

@0xApotheosis 0xApotheosis commented Jan 7, 2026

Copy link
Copy Markdown
Member

Description

Adds a new @shapeshiftoss/public-api package that exposes swap functionality as a REST API for external partners.

Production URL: https://api.shapeshift.com
API Documentation: https://api.shapeshift.com/docs

Endpoints:

  • GET /health - Health check
  • GET /v1/assets - List all supported assets
  • GET /v1/swap/rates - Fetch rates from all enabled swappers (0x, Relay, THORChain, etc.)
  • GET /v1/swap/quote - Get executable quote with full transaction data
  • GET /docs - Interactive Scalar API documentation

Architecture:

  • Imports @shapeshiftoss/swapper directly - zero code duplication
  • Server-side SwapperDeps with minimal EVM adapters fetching gas fees from Unchained
  • esbuild bundling into single CJS file for deployment
  • Multi-stage Docker build with BuildKit cache optimization
  • Scalar-powered interactive API docs with zod-to-openapi

Note: Affiliate fee sharing is not yet implemented. Currently all swaps use the standard DAO affiliate fee (55 bps). Partner attribution, fee tracking, and revenue sharing will be added in Phase 2.

Railway Config Changes:

  • Moved swap-widget Railway config from root railway.json into packages/swap-widget/railway.toml
  • Deleted root-level railway.json which was overriding the public-api package config
  • This ensures each service uses its package-level Railway config without conflicts

Issue (if applicable)

closes #

Risk

Low-Medium - This is a new standalone package/service that doesn't modify any existing swap flows in the web app.

What protocols, transaction types, wallets or contract interactions might be affected by this PR?

None directly. The public API reads from existing swapper infrastructure but doesn't introduce new on-chain transactions.

Testing

Engineering

# Health check
curl https://api.shapeshift.com/health

# Get rates (ETH -> USDC)
curl "https://api.shapeshift.com/v1/swap/rates?sellAssetId=eip155:1/slip44:60&buyAssetId=eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48&sellAmountCryptoBaseUnit=1000000000000000000"

# Interactive docs
open https://api.shapeshift.com/docs

Operations

  • 🏁 My feature is behind a flag and doesn't require operations testing (yet)

This is a new standalone service deployed to Railway. It does not affect the main web application.

Screenshots (if applicable)

N/A - API service, no UI changes.

Summary by CodeRabbit

  • New Features

    • Public API: assets, rates, and quote endpoints with API-key auth, OpenAPI docs/UI, and a smoke-test suite.
  • Infrastructure

    • Production Docker multi-stage build, Docker ignore, esbuild bundling, healthchecks, and a standalone mock server for local testing.
  • Chores

    • Sample env and TypeScript configs, API types/middleware, test utilities/runners, docs, and dependency/import fixes (lodash → lodash‑es).

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Jan 7, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@0xApotheosis has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 24 minutes and 32 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 3545771 and 318e59c.

📒 Files selected for processing (1)
  • packages/public-api/src/swapperDeps.ts
📝 Walkthrough

Walkthrough

Adds a new packages/public-api Express service with assets, rates, quote, docs endpoints, API-key auth, OpenAPI generation, server-side swapper dependency plumbing, build tooling (esbuild, Docker), smoke tests, types, and several monorepo and swapper package adjustments.

Changes

Cohort / File(s) Summary
Repo ignore
/.gitignore
Added .playwright-mcp/ ignore entry.
Public API package manifest & config
packages/public-api/package.json, packages/public-api/tsconfig.json, packages/public-api/.env.example
New package manifest, scripts, TypeScript config, and example env vars.
Build & Docker
packages/public-api/Dockerfile, packages/public-api/Dockerfile.dockerignore, packages/public-api/esbuild.config.mjs, packages/public-api/esbuild.smoke-tests.mjs
Multi-stage Dockerfile, dockerignore, and esbuild build scripts for server and smoke-tests.
Server bootstrap & routes
packages/public-api/src/index.ts, packages/public-api/src/server-standalone.ts, packages/public-api/src/routes/*
New Express server entry, standalone mock server, and route modules for assets, rates, quote, docs, and health.
Asset loading, config & types
packages/public-api/src/assets.ts, packages/public-api/src/config.ts, packages/public-api/src/types.ts, packages/public-api/src/lib/zod.ts, packages/public-api/src/setupZod.ts
Lazy asset loader/enrichment, env-derived server config/constants, public API types, boolean parsing zod schema, and Zod→OpenAPI setup.
Auth middleware
packages/public-api/src/middleware/auth.ts
Required and optional API-key middlewares attaching PartnerConfig to requests.
Swapper deps & handlers
packages/public-api/src/swapperDeps.ts, packages/public-api/src/routes/quote.ts, packages/public-api/src/routes/rates.ts
createServerSwapperDeps with minimal EVM adapter and stubs; lazy swapper loading in rates/quote handlers, per-swapper orchestration and error handling.
OpenAPI & docs UI
packages/public-api/src/docs/openapi.ts, packages/public-api/src/routes/docs.ts
OpenAPI v3 document generation and Scalar UI route exposing /json and UI.
Smoke tests & test utils
packages/public-api/tests/*
Smoke-test definitions, runner, utilities, and CI-friendly runner script.
Build guidance & docs
packages/public-api/CLAUDE.md
Local run and LLM guidance for public-api.
Railway / deploy config
packages/public-api/railway.toml, packages/swap-widget/railway.toml, railway.json
Added per-package Railway configs; removed root railway.json.
Swapper package adjustments
packages/swapper/package.json, packages/swapper/src/swappers/CetusSwapper/*
Added @mysten/sui dependency; updated CetusSwapper import paths.
Utils package migration
packages/utils/package.json, packages/utils/src/*
Replace lodashlodash-es and update imports/types.
Unchained client tweaks
packages/unchained-client/generator/post_process.sh, packages/unchained-client/package.json
Add script shebang/newline and docker-friendly npm scripts.
Minor swapper/logic tweaks
packages/swapper/src/swappers/*
Error-guarding and pre-flight chain validation changes in Portals/Chainflip/Cetus swapper code.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor Client
    participant Router as "API Router"
    participant Auth as "Auth Middleware"
    participant Assets as "Asset Service"
    participant Swapper as "Swapper Module"
    participant External as "External Services"

    Client->>Router: GET /v1/swap/rates + X-API-Key
    Router->>Auth: validate X-API-Key
    Auth-->>Router: PartnerConfig
    Router->>Assets: getAsset(sellAssetId), getAsset(buyAssetId)
    Assets-->>Router: asset data
    Router->>Swapper: lazy-load swapper, request rates
    Swapper->>External: fetch rates / gas / chain data
    External-->>Swapper: responses or errors
    Swapper-->>Router: aggregated per-swapper rates
    Router-->>Client: RatesResponse (sorted, errors annotated)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • NeOMakinG
  • gomesalexandre
  • kaladinlight

Poem

🐇 I hopped through nodes and envs so bright,
I bundled routes and keys by night,
Docs and tests tucked in a nest,
Docker built and smoke-tests blessed,
A rabbit's patch — the API takes flight!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: add public swap API server' clearly and concisely summarizes the main change: addition of a new public REST API for swap functionality. It is specific, follows conventional commit format, and accurately represents the primary feature being introduced.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/public-swap-api

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@railway-app
railway-app Bot temporarily deployed to microservices / production January 7, 2026 06:14 Inactive
@railway-app
railway-app Bot temporarily deployed to microservices / production January 7, 2026 06:35 Inactive
@railway-app
railway-app Bot temporarily deployed to microservices / production January 7, 2026 06:56 Inactive
@railway-app
railway-app Bot temporarily deployed to microservices / production January 7, 2026 06:59 Inactive
@railway-app
railway-app Bot temporarily deployed to microservices / production January 7, 2026 07:17 Inactive
@railway-app
railway-app Bot temporarily deployed to microservices / production January 7, 2026 07:18 Inactive
@railway-app
railway-app Bot temporarily deployed to microservices / production January 7, 2026 07:25 Inactive
@railway-app
railway-app Bot temporarily deployed to microservices / production January 7, 2026 07:43 Inactive
@railway-app
railway-app Bot temporarily deployed to microservices / production January 7, 2026 08:14 Inactive
@railway-app
railway-app Bot temporarily deployed to microservices / production January 7, 2026 23:39 Inactive
@railway-app
railway-app Bot temporarily deployed to microservices / production January 7, 2026 23:42 Inactive
@railway-app
railway-app Bot temporarily deployed to microservices / production January 8, 2026 00:16 Inactive
@railway-app
railway-app Bot temporarily deployed to microservices / production January 16, 2026 06:58 Inactive
@railway-app
railway-app Bot temporarily deployed to microservices / production January 19, 2026 06:04 Inactive
0xApotheosis and others added 14 commits January 19, 2026 17:14
Implements a public API for swap quotes and rates that reuses the existing
@shapeshiftoss/swapper package without code duplication.

Endpoints:
- GET /v1/swap/rates - Get rates from all swappers
- POST /v1/swap/quote - Get executable quote with tx data
- GET /v1/assets - List supported assets

Features:
- API key authentication middleware
- Minimal EVM chain adapters for gas fee estimation
- esbuild bundling for single-file deployment
- Docker + Railway deployment configuration
- 55 bps affiliate fee (planned 50/50 split with partners)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Replace YARN_ENABLE_SCRIPTS=false with --mode skip-build to properly skip
  all lifecycle scripts including postinstall that requires git
- Add tsconfig files copy for TypeScript compilation
- Add build:docker script to unchained-client to skip Java-based code generation
- Use explicit workspace builds instead of yarn workspaces foreach
- Type JSON response in swapperDeps.ts to fix 'data is of type unknown' error
- Add GetTradeRateInput type assertion in rates.ts for chainId compatibility
- Add GetTradeQuoteInputWithWallet type assertion in quote.ts for chainId compatibility
- Install openjdk17-jre in build stage for OpenAPI generator CLI
- Use full unchained-client build script instead of build:docker
…process.sh

Fixes 'Exec format error' during Docker build.
Fixes runtime module not found errors for external dependencies like @cowprotocol/app-data
…filled examples

- Add Scalar API reference UI at /docs endpoint
- Configure defaultOpenAllTags to auto-expand all sections on page load
- Prefill test API key (test-api-key-123) in authentication section
- Add OpenAPI example values to all request schemas for prefilled forms
- Set up zod-to-openapi for generating OpenAPI spec from Zod schemas

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
zod-to-openapi v8.x requires zod v4 as a peer dependency, causing runtime
errors when used with zod v3.23.8. Downgrade to v7.3.4 which supports zod v3.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
esbuild bundles all dependencies into server.cjs, so copying the entire
node_modules directory to the production image was unnecessary and added
significant time to Railway deployments (~2-4 min) and image size (~500MB+).

The only externals are fsevents (macOS-only, not used in Linux containers)
and @cowprotocol/* (not used by public-api routes).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add comprehensive description explaining how to integrate the swap API:
- Step-by-step integration flow (assets -> rates -> quote -> execute)
- Authentication requirements
- CAIP-19 asset ID format with examples

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add Dockerfile.dockerignore to exclude node_modules (4.5GB), .git (1.4GB),
  and other unnecessary files from build context
- Enable BuildKit with syntax directive for advanced features
- Add cache mount for yarn berry cache to speed up subsequent builds

These optimizations reduce build context transfer time and cache dependencies
between builds, further improving Railway deployment speed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Railway requires cache mounts to have an explicit id parameter.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Railway's cache mount requires a specific prefix format that's not
documented. Reverting to standard yarn install - the .dockerignore
still provides significant build context reduction.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Query parameters are always received as strings in Express. The Zod
schemas for rates and quote endpoints expected actual boolean/number
types, causing validation errors when allowMultiHop was passed as
"true" or "false" string from the docs example.

- Create shared booleanFromString utility to preprocess string booleans
- Update rates.ts to use booleanFromString for allowMultiHop
- Update quote.ts to use booleanFromString and z.coerce.number()

Co-Authored-By: Claude <noreply@anthropic.com>
@railway-app
railway-app Bot temporarily deployed to microservices / production January 19, 2026 08:46 Inactive
…oded values

Allow staging/multi-tenant environments to override EVM chain endpoints via
environment variables while preserving existing defaults as fallbacks.

Co-Authored-By: Claude <noreply@anthropic.com>
@railway-app
railway-app Bot temporarily deployed to microservices / production January 19, 2026 08:50 Inactive
@railway-app
railway-app Bot temporarily deployed to microservices / production January 19, 2026 09:10 Inactive

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@packages/public-api/src/routes/rates.ts`:
- Around line 136-151: Replace the unsafe any-cast fallback for error.code in
the error object: instead of error.code || ('UnknownError' as any), use nullish
coalescing to provide the properly-typed fallback (e.g., error.code ??
TradeQuoteError.UnknownError or a typed 'UnknownError' constant). Update the
construction in the block that handles result.isErr() (where you call
result.unwrapErr() and return the error object alongside swapperName, rate,
etc.) so the fallback value is correctly typed and no longer casts to any.
- Around line 12-20: The RatesRequestSchema currently allows any non-empty
string for sellAmountCryptoBaseUnit and slippageTolerancePercentageDecimal; add
strict regex validation to these fields so malformed numeric input is rejected
at the API boundary: update RatesRequestSchema to replace
sellAmountCryptoBaseUnit: z.string().min(1) with a regex that only permits a
positive integer (e.g., /^\d+$/) and replace slippageTolerancePercentageDecimal:
z.string().optional() with a regex that permits a non-negative decimal number
(e.g., /^(?:\d+)(?:\.\d+)?$/) while preserving optional/default behavior; use
z.string().regex(...) (or z.string().refine(...)) so Zod returns a 400 on
invalid formats and leave allowMultiHop and other fields unchanged.

In `@packages/public-api/src/swapperDeps.ts`:
- Around line 52-75: The fetchGasFees function currently uses a raw fetch
without a timeout; replace it with the project's timeout pattern by invoking the
existing fetchWithTimeout (or implement the same AbortController pattern) when
calling `${unchainedUrl}/api/v1/gas/fees` inside fetchGasFees so the request is
aborted on timeout; align the timeout duration with other swapper operations
(e.g., the 60s used by timeoutMonadic or choose a context-appropriate value) and
preserve the same error handling behavior (throwing an Error when response.ok is
false) while removing any comment/claim that fetch isn't available in Node 22.
🧹 Nitpick comments (2)
packages/public-api/src/swapperDeps.ts (1)

1-30: Align gas-fee types with chain-adapter exports and add explicit helper return types.

Local GasFeeData/GasFeeDataEstimate can drift from chain-adapters’ canonical types; also the helper functions lack explicit return types. Consider importing the shared types (if exported) and annotating helper returns to keep the contract tight. As per coding guidelines, use explicit function return types.

♻️ Suggested refactor
-import type {
-  ChainAdapter,
-  CosmosSdkChainAdapter,
-  EvmChainAdapter,
-  near,
-  solana,
-  starknet,
-  sui,
-  ton,
-  tron,
-  UtxoChainAdapter,
-} from '@shapeshiftoss/chain-adapters'
+import type {
+  ChainAdapter,
+  CosmosSdkChainAdapter,
+  EvmChainAdapter,
+  GasFeeData,
+  GasFeeDataEstimate,
+  near,
+  solana,
+  starknet,
+  sui,
+  ton,
+  tron,
+  UtxoChainAdapter,
+} from '@shapeshiftoss/chain-adapters'
@@
-type GasFeeData = {
-  gasPrice: string
-  maxFeePerGas?: string
-  maxPriorityFeePerGas?: string
-}
-
-type GasFeeDataEstimate = {
-  fast: GasFeeData
-  average: GasFeeData
-  slow: GasFeeData
-}
+type MinimalEvmChainAdapter = Pick<
+  EvmChainAdapter,
+  'getChainId' | 'getGasFeeData' | 'getFeeAssetId' | 'getDisplayName'
+>
@@
-const createMinimalEvmAdapter = (chainId: ChainId) => {
+const createMinimalEvmAdapter = (chainId: ChainId): MinimalEvmChainAdapter => {
@@
-const createStubAdapter = (type: string) => {
+const createStubAdapter = (type: string): ((chainId: ChainId) => never) => {

Also applies to: 77-122

packages/public-api/src/routes/rates.ts (1)

39-47: Add explicit return types for lazy-import helpers.

These helpers currently rely on inference; add explicit return types to keep the module boundary tight. As per coding guidelines, use explicit function return types.

♻️ Suggested typing
-let swapperModule: Awaited<ReturnType<typeof importSwapperModule>> | null = null
-const importSwapperModule = () => import('@shapeshiftoss/swapper')
-const getSwapperModule = async () => {
+let swapperModule: Awaited<ReturnType<typeof importSwapperModule>> | null = null
+const importSwapperModule = (): Promise<typeof import('@shapeshiftoss/swapper')> =>
+  import('@shapeshiftoss/swapper')
+const getSwapperModule = async (): Promise<typeof import('@shapeshiftoss/swapper')> => {
   if (!swapperModule) {
     swapperModule = await importSwapperModule()
   }
   return swapperModule
 }

Comment thread packages/public-api/src/routes/rates.ts
Comment thread packages/public-api/src/routes/rates.ts
Comment thread packages/public-api/src/swapperDeps.ts
Change the sample request in the OpenAPI docs from ETH→USDC to ETH→BTC
using the Relay swapper, with appropriate Bitcoin receive address.

Co-Authored-By: Claude <noreply@anthropic.com>
@railway-app
railway-app Bot temporarily deployed to microservices / production January 19, 2026 09:21 Inactive

@NeOMakinG NeOMakinG left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been consuming the stuff since a few days already, really happy to get that in for the sake of progression

Gave it a bit of a smoke test in https://jam.dev/c/a8071036-ee77-4dfb-b2a0-9c185963846d even thought it's missing a lot of parts (BTC, SOL, any other chains), we will be able to test it more as long as we add more chains support from the widget (there is an open PR for Solana and BTC)

Comment thread .gitignore
Comment on lines +19 to +21
# Partner API Keys (add your partner keys here)
# Format: API_KEY_<PARTNER_ID>=<api_key>:<name>:<fee_share_percentage>
# Example: API_KEY_PARTNER1=abc123:MyPartner:50

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure it's a reliable way, we will need to add a way to generate API keys automatically or a sort of dashboard to ease the integration

If we have a polished product, we could run a marketing campaign and see if it hooks

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For sure, we'll definitely have an actual dashboard for this soon - this is just MVP stuff!

Comment on lines +20 to +22
path.join(process.cwd(), 'public/generated/generatedAssetData.json'),
path.join(process.cwd(), '../../public/generated/generatedAssetData.json'),
path.join(process.cwd(), 'generatedAssetData.json'),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

q: what happens if the project isn't rebuilt because not caught up by the railway stuff when we merge asset data PRs? Are we getting out of data assets?

We can probably fix that by fetching the generatedAssetData.json as a XHR to app.shapeshift.com then cache it, so we ensure we always use the latest upstream dataset


// Static API keys for testing (in production these would come from a database)
export const STATIC_API_KEYS: Record<string, { name: string; feeSharePercentage: number }> = {
'test-api-key-123': { name: 'Test Partner', feeSharePercentage: 50 },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the future we will probably want fees to be configurable at quote time with our base fees on top of that

// Asset endpoints (optional auth)
v1Router.get('/assets', optionalApiKeyAuth, getAssets)
v1Router.get('/assets/count', optionalApiKeyAuth, getAssetCount)
v1Router.get('/assets/:assetId(*)', optionalApiKeyAuth, getAssetById)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will probably want to add some cache on top of that at some point

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will want to provide a way for our users to check the market price of any asset

Comment on lines +23 to +33
const ENABLED_SWAPPER_NAMES = [
'THORChain',
'MAYAChain',
'0x',
'CoW Swap',
'Portals',
'Chainflip',
'Jupiter',
'Relay',
'ButterSwap',
'Bebop',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heyyyy, we need to make it better! It's missing a few swappers there, is this something we are planning to bring soon?

You can remove Jupiter already if you want, it's not working anymore and won't be working as supported by relay under the hood

Comment on lines +115 to +122
const createStubAdapter = (type: string) => {
return () => {
throw new Error(
`Chain adapter ${type} not implemented in public API. ` +
`This swapper requires chain adapter functionality that is not yet available.`,
)
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What, in the end we will 100% be able to use the chain adapters directly, providing more accurate fee estimation etc, but do we even need them?

@railway-app
railway-app Bot temporarily deployed to microservices / develop January 19, 2026 23:02 Inactive
THORChain BTC sell trades were failing because assertGetUtxoChainAdapter
was a stub that threw an error. This implements a minimal UTXO adapter
that provides the required interface for rate requests.

Co-Authored-By: Claude <noreply@anthropic.com>
@railway-app
railway-app Bot temporarily deployed to microservices / production January 19, 2026 23:39 Inactive
…inflip

- Update default THORChain Midgard URL to ninerealms.com for reliability
- Fix Portals swapper to validate chain support before calling EVM adapter
- Fix Chainflip swapper to handle undefined error properties with optional chaining

Co-Authored-By: Claude <noreply@anthropic.com>
@railway-app
railway-app Bot temporarily deployed to microservices / production January 20, 2026 00:00 Inactive
Validate sellAmountCryptoBaseUnit with /^\d+$/ to only accept positive integers
and slippageTolerancePercentageDecimal with /^(?:\d+)(?:\.\d+)?$/ to accept
non-negative decimals, rejecting malformed numeric input at the API boundary.

Co-Authored-By: Claude <noreply@anthropic.com>
@railway-app
railway-app Bot temporarily deployed to microservices / production January 20, 2026 00:05 Inactive
…allback

Use nullish coalescing with TradeQuoteError.UnknownError instead of
casting 'UnknownError' as any for proper type safety.

Co-Authored-By: Claude <noreply@anthropic.com>
@railway-app
railway-app Bot temporarily deployed to microservices / production January 20, 2026 00:10 Inactive
Prevents hanging requests when unchained gas fees endpoint is slow/unresponsive.
Uses 10s timeout consistent with RATE_TIMEOUT_MS in rates route.

Co-Authored-By: Claude <noreply@anthropic.com>
@railway-app
railway-app Bot temporarily deployed to microservices / production January 20, 2026 00:14 Inactive
@0xApotheosis

Copy link
Copy Markdown
Member Author

Thanks @NeOMakinG, I'll merge this guy and tackle your comments in a follow-up PR.

@0xApotheosis
0xApotheosis merged commit a8a7971 into develop Jan 20, 2026
5 checks passed
@0xApotheosis
0xApotheosis deleted the feat/public-swap-api branch January 20, 2026 00:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants