diff --git a/packages/public-api/src/config.ts b/packages/public-api/src/config.ts index 7c323189e3c..d20f8876695 100644 --- a/packages/public-api/src/config.ts +++ b/packages/public-api/src/config.ts @@ -58,8 +58,3 @@ export const DEFAULT_AFFILIATE_BPS = '60' // API server config export const API_PORT = parseInt(process.env.PORT || '3001', 10) export const API_HOST = process.env.HOST || '0.0.0.0' - -// Static API keys for testing (in production these would come from a database) -export const STATIC_API_KEYS: Record = { - 'test-api-key-123': { name: 'Test Partner', feeSharePercentage: 50 }, -} diff --git a/packages/public-api/src/docs/openapi.ts b/packages/public-api/src/docs/openapi.ts index 9e01a0700ff..f92a7c1880d 100644 --- a/packages/public-api/src/docs/openapi.ts +++ b/packages/public-api/src/docs/openapi.ts @@ -13,13 +13,6 @@ export const registry = new OpenAPIRegistry() // We should probably define the response schemas with Zod too, but for now we'll do best effort with the request schemas // and basic response structures. -// Security Schemes -registry.registerComponent('securitySchemes', 'apiKeyAuth', { - type: 'apiKey', - in: 'header', - name: 'X-API-Key', -}) - // --- Definitions --- // Asset @@ -305,7 +298,6 @@ registry.registerPath({ description: 'Get informative swap rates from all available swappers. This does not create a transaction.', tags: ['Swaps'], - security: [{ apiKeyAuth: [] }], request: { query: RatesRequestSchema, }, @@ -332,7 +324,6 @@ registry.registerPath({ description: 'Get an executable quote for a swap, including transaction data. Requires a specific swapper name.', tags: ['Swaps'], - security: [{ apiKeyAuth: [] }], request: { body: { content: { @@ -405,9 +396,6 @@ POST /v1/swap/quote ### 5. Execute the Swap Use the returned \`transactionData\` to build and sign a transaction with the user's wallet, then broadcast it to the network. -## Authentication -Include your API key in the \`X-API-Key\` header for all swap endpoints. - ## Asset IDs Assets use CAIP-19 format: \`{chainId}/{assetNamespace}:{assetReference}\` - Native ETH: \`eip155:1/slip44:60\` diff --git a/packages/public-api/src/index.ts b/packages/public-api/src/index.ts index 756007aa52f..a54e6f176ff 100644 --- a/packages/public-api/src/index.ts +++ b/packages/public-api/src/index.ts @@ -5,7 +5,6 @@ import express from 'express' import { initAssets } from './assets' import { API_HOST, API_PORT } from './config' -import { apiKeyAuth, optionalApiKeyAuth } from './middleware/auth' import { getAssetById, getAssetCount, getAssets } from './routes/assets' import { getChainCount, getChains } from './routes/chains' import { docsRouter } from './routes/docs' @@ -38,7 +37,7 @@ app.get('/', (_req, res) => { }) }) -// Health check (no auth required) +// Health check app.get('/health', (_req, res) => { res.json({ status: 'ok', timestamp: Date.now() }) }) @@ -46,18 +45,18 @@ app.get('/health', (_req, res) => { // API v1 routes const v1Router = express.Router() -// Swap endpoints (require API key) -v1Router.get('/swap/rates', apiKeyAuth, getRates) -v1Router.post('/swap/quote', apiKeyAuth, getQuote) +// Swap endpoints +v1Router.get('/swap/rates', getRates) +v1Router.post('/swap/quote', getQuote) -// Chain endpoints (optional auth) -v1Router.get('/chains', optionalApiKeyAuth, getChains) -v1Router.get('/chains/count', optionalApiKeyAuth, getChainCount) +// Chain endpoints +v1Router.get('/chains', getChains) +v1Router.get('/chains/count', getChainCount) -// Asset endpoints (optional auth) -v1Router.get('/assets', optionalApiKeyAuth, getAssets) -v1Router.get('/assets/count', optionalApiKeyAuth, getAssetCount) -v1Router.get('/assets/:assetId(*)', optionalApiKeyAuth, getAssetById) +// Asset endpoints +v1Router.get('/assets', getAssets) +v1Router.get('/assets/count', getAssetCount) +v1Router.get('/assets/:assetId(*)', getAssetById) app.use('/v1', v1Router) app.use('/docs', docsRouter) @@ -90,10 +89,6 @@ Available endpoints: GET /v1/assets - List supported assets GET /v1/assets/count - Get asset count GET /v1/assets/:assetId - Get single asset by ID - -Authentication: - Include 'X-API-Key' header with your API key for /v1/swap/* endpoints. - Test API key: test-api-key-123 `) }) } diff --git a/packages/public-api/src/middleware/auth.ts b/packages/public-api/src/middleware/auth.ts deleted file mode 100644 index c859cc5c66e..00000000000 --- a/packages/public-api/src/middleware/auth.ts +++ /dev/null @@ -1,83 +0,0 @@ -import crypto from 'crypto' -import type { NextFunction, Request, Response } from 'express' - -import { STATIC_API_KEYS } from '../config' -import type { ErrorResponse, PartnerConfig } from '../types' - -// Hash an API key for comparison (in production, keys would be stored hashed) -const hashApiKey = (key: string): string => { - return crypto.createHash('sha256').update(key).digest('hex') -} - -// API key authentication middleware -export const apiKeyAuth = (req: Request, res: Response, next: NextFunction): void => { - const apiKey = req.header('X-API-Key') - - if (!apiKey) { - const errorResponse: ErrorResponse = { - error: 'API key required', - code: 'MISSING_API_KEY', - } - res.status(401).json(errorResponse) - return - } - - // Look up the partner by API key - // In production, this would query a database - const partnerInfo = STATIC_API_KEYS[apiKey] - - if (!partnerInfo) { - const errorResponse: ErrorResponse = { - error: 'Invalid API key', - code: 'INVALID_API_KEY', - } - res.status(401).json(errorResponse) - return - } - - // Attach partner info to request - const partner: PartnerConfig = { - id: hashApiKey(apiKey).substring(0, 16), // Use hash prefix as ID - apiKeyHash: hashApiKey(apiKey), - name: partnerInfo.name, - feeSharePercentage: partnerInfo.feeSharePercentage, - status: 'active', - rateLimit: { - requestsPerMinute: 60, - requestsPerDay: 10000, - }, - createdAt: new Date(), - } - - req.partner = partner - - next() -} - -// Optional auth - allows unauthenticated requests but attaches partner info if present -export const optionalApiKeyAuth = (req: Request, _res: Response, next: NextFunction): void => { - const apiKey = req.header('X-API-Key') - - if (apiKey) { - const partnerInfo = STATIC_API_KEYS[apiKey] - - if (partnerInfo) { - const partner: PartnerConfig = { - id: hashApiKey(apiKey).substring(0, 16), - apiKeyHash: hashApiKey(apiKey), - name: partnerInfo.name, - feeSharePercentage: partnerInfo.feeSharePercentage, - status: 'active', - rateLimit: { - requestsPerMinute: 60, - requestsPerDay: 10000, - }, - createdAt: new Date(), - } - - req.partner = partner - } - } - - next() -} diff --git a/packages/public-api/src/routes/docs.ts b/packages/public-api/src/routes/docs.ts index 0434728335f..65dbcf56bf1 100644 --- a/packages/public-api/src/routes/docs.ts +++ b/packages/public-api/src/routes/docs.ts @@ -26,12 +26,6 @@ router.use( hideDownloadButton: true, darkMode: true, defaultOpenAllTags: true, - authentication: { - preferredSecurityScheme: 'apiKeyAuth', - apiKey: { - token: 'test-api-key-123', - }, - }, customCss: ` .sidebar { --theme-color-1: #383838; } `, diff --git a/packages/public-api/src/server-standalone.ts b/packages/public-api/src/server-standalone.ts index f7eb5c2af54..e32a9a33094 100644 --- a/packages/public-api/src/server-standalone.ts +++ b/packages/public-api/src/server-standalone.ts @@ -7,53 +7,25 @@ */ import cors from 'cors' -import crypto from 'crypto' import express from 'express' import { v4 as uuidv4 } from 'uuid' const API_PORT = parseInt(process.env.PORT || '3001', 10) const API_HOST = process.env.HOST || '0.0.0.0' -// Static API keys for testing -const STATIC_API_KEYS: Record = { - 'test-api-key-123': { name: 'Test Partner', feeSharePercentage: 50 }, -} - const app = express() // Middleware app.use(cors()) app.use(express.json()) -// API key auth middleware -const apiKeyAuth = (req: express.Request, res: express.Response, next: express.NextFunction) => { - const apiKey = req.header('X-API-Key') - - if (!apiKey) { - return res.status(401).json({ error: 'API key required', code: 'MISSING_API_KEY' }) - } - - const partnerInfo = STATIC_API_KEYS[apiKey] - if (!partnerInfo) { - return res.status(401).json({ error: 'Invalid API key', code: 'INVALID_API_KEY' }) - } - - ;(req as any).partner = { - id: crypto.createHash('sha256').update(apiKey).digest('hex').substring(0, 16), - name: partnerInfo.name, - feeSharePercentage: partnerInfo.feeSharePercentage, - } - - next() -} - // Health check app.get('/health', (_req, res) => { res.json({ status: 'ok', timestamp: Date.now() }) }) // Mock rates endpoint -app.get('/v1/swap/rates', apiKeyAuth, (req, res) => { +app.get('/v1/swap/rates', (req, res) => { const { sellAssetId, buyAssetId, sellAmountCryptoBaseUnit } = req.query if (!sellAssetId || !buyAssetId || !sellAmountCryptoBaseUnit) { @@ -108,7 +80,7 @@ app.get('/v1/swap/rates', apiKeyAuth, (req, res) => { }) // Mock quote endpoint -app.post('/v1/swap/quote', apiKeyAuth, (req, res) => { +app.post('/v1/swap/quote', (req, res) => { const { sellAssetId, buyAssetId, sellAmountCryptoBaseUnit, receiveAddress, swapperName } = req.body @@ -209,34 +181,24 @@ app.use((_req, res) => { // Start server app.listen(API_PORT, API_HOST, () => { console.log(` -╔══════════════════════════════════════════════════════════════════╗ -║ ShapeShift Public Swap API ║ -║ (Mock/Test Server) ║ -╚══════════════════════════════════════════════════════════════════╝ - Server running at http://${API_HOST}:${API_PORT} Endpoints: - GET /health - Health check (no auth) + GET /health - Health check GET /v1/swap/rates - Get swap rates from all swappers POST /v1/swap/quote - Get executable quote with tx data GET /v1/assets - List supported assets -Authentication: - Include 'X-API-Key' header for /v1/swap/* endpoints - Test key: test-api-key-123 - Example requests: # Health check curl http://localhost:${API_PORT}/health # Get rates - curl -H "X-API-Key: test-api-key-123" \\ - "http://localhost:${API_PORT}/v1/swap/rates?sellAssetId=eip155:1/slip44:60&buyAssetId=eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48&sellAmountCryptoBaseUnit=1000000000000000000" + curl "http://localhost:${API_PORT}/v1/swap/rates?sellAssetId=eip155:1/slip44:60&buyAssetId=eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48&sellAmountCryptoBaseUnit=1000000000000000000" # Get quote - curl -X POST -H "X-API-Key: test-api-key-123" -H "Content-Type: application/json" \\ + curl -X POST -H "Content-Type: application/json" \\ -d '{"sellAssetId":"eip155:1/slip44:60","buyAssetId":"eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48","sellAmountCryptoBaseUnit":"1000000000000000000","receiveAddress":"0x742d35Cc6634C0532925a3b844Bc9e7595f4EdC3","swapperName":"THORChain"}' \\ http://localhost:${API_PORT}/v1/swap/quote diff --git a/packages/public-api/src/types.ts b/packages/public-api/src/types.ts index 540710510e7..4330c2ef493 100644 --- a/packages/public-api/src/types.ts +++ b/packages/public-api/src/types.ts @@ -23,19 +23,6 @@ export type { UtxoTransactionData, } -export type PartnerConfig = { - id: string - apiKeyHash: string - name: string - feeSharePercentage: number - status: 'active' | 'suspended' | 'pending' - rateLimit: { - requestsPerMinute: number - requestsPerDay: number - } - createdAt: Date -} - export type RatesRequest = { sellAssetId: AssetId buyAssetId: AssetId @@ -176,11 +163,3 @@ export type ErrorResponse = { code?: string details?: unknown } - -declare global { - namespace Express { - interface Request { - partner?: PartnerConfig - } - } -} diff --git a/packages/public-api/tests/smoke-tests.ts b/packages/public-api/tests/smoke-tests.ts index 809d48ac626..e467df7ebeb 100644 --- a/packages/public-api/tests/smoke-tests.ts +++ b/packages/public-api/tests/smoke-tests.ts @@ -1,4 +1,4 @@ -import { ASSET_IDS, TEST_API_KEY, TEST_PAIRS } from './test-config' +import { ASSET_IDS, TEST_PAIRS } from './test-config' import type { TestResult, TestSuiteResult } from './test-utils' import { fetchWithTimeout, runTest } from './test-utils' @@ -85,38 +85,7 @@ export const runSmokeTests = async (): Promise => { }), ) - // 7. Rates Auth Check (Critical) - results.push( - await runTest('Rates requires auth', true, async () => { - const params = new URLSearchParams({ - sellAssetId: ASSET_IDS.ETH, - buyAssetId: ASSET_IDS.USDC_ETH, - sellAmountCryptoBaseUnit: '100000000000000000', - }) - const res = await fetchWithTimeout(`${API_URL}/v1/swap/rates?${params}`, {}) - if (res.status !== 401) throw new Error(`Expected 401, got ${res.status}`) - }), - ) - - // 8. Quote Auth Check (Critical) - results.push( - await runTest('Quote requires auth', true, async () => { - const res = await fetchWithTimeout(`${API_URL}/v1/swap/quote`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - sellAssetId: ASSET_IDS.ETH, - buyAssetId: ASSET_IDS.USDC_ETH, - sellAmountCryptoBaseUnit: '100000000000000000', - receiveAddress: '0x0000000000000000000000000000000000000000', - swapperName: '0x', - }), - }) - if (res.status !== 401) throw new Error(`Expected 401, got ${res.status}`) - }), - ) - - // 9. EVM Same-Chain Rates (Informative) + // 7. EVM Same-Chain Rates (Informative) const evmPair = TEST_PAIRS.evmSameChain[0] results.push( await runTest(`Rates: ${evmPair.name}`, false, async () => { @@ -125,13 +94,7 @@ export const runSmokeTests = async (): Promise => { buyAssetId: evmPair.buyAssetId, sellAmountCryptoBaseUnit: evmPair.sellAmountCryptoBaseUnit, }) - const res = await fetchWithTimeout( - `${API_URL}/v1/swap/rates?${params}`, - { - headers: { 'X-API-Key': TEST_API_KEY }, - }, - 30000, - ) // 30s timeout for rates + const res = await fetchWithTimeout(`${API_URL}/v1/swap/rates?${params}`, {}, 30000) // 30s timeout for rates if (!res.ok) throw new Error(`Rates request failed: ${res.status}`) const data = (await res.json()) as { @@ -166,7 +129,7 @@ export const runSmokeTests = async (): Promise => { }), ) - // 10. Cross-Chain Rates (Informative) + // 8. Cross-Chain Rates (Informative) const crossChainPair = TEST_PAIRS.crossChain[0] results.push( await runTest(`Rates: ${crossChainPair.name}`, false, async () => { @@ -175,13 +138,7 @@ export const runSmokeTests = async (): Promise => { buyAssetId: crossChainPair.buyAssetId, sellAmountCryptoBaseUnit: crossChainPair.sellAmountCryptoBaseUnit, }) - const res = await fetchWithTimeout( - `${API_URL}/v1/swap/rates?${params}`, - { - headers: { 'X-API-Key': TEST_API_KEY }, - }, - 30000, - ) + const res = await fetchWithTimeout(`${API_URL}/v1/swap/rates?${params}`, {}, 30000) if (!res.ok) throw new Error(`Rates request failed: ${res.status}`) const data = (await res.json()) as { diff --git a/packages/public-api/tests/test-config.ts b/packages/public-api/tests/test-config.ts index 7ce270ba947..2ca4eb95215 100644 --- a/packages/public-api/tests/test-config.ts +++ b/packages/public-api/tests/test-config.ts @@ -1,5 +1,3 @@ -export const TEST_API_KEY = 'test-api-key-123' - export const ASSET_IDS = { // EVM Native Assets ETH: 'eip155:1/slip44:60', diff --git a/packages/swap-widget/README.md b/packages/swap-widget/README.md index 4c0ae3b5118..202e5f2178c 100644 --- a/packages/swap-widget/README.md +++ b/packages/swap-widget/README.md @@ -44,7 +44,6 @@ import { SwapWidget } from "@shapeshiftoss/swap-widget"; function App() { return ( console.log("Success:", txHash)} onSwapError={(error) => console.error("Error:", error)} @@ -59,7 +58,6 @@ function App() { | Prop | Type | Default | Description | | ------------------------ | ----------------------------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------- | -| `apiKey` | `string` | - | ShapeShift API key for fetching swap rates. Required for production use. | | `apiBaseUrl` | `string` | - | Custom API base URL. Useful for testing or custom deployments. | | `defaultSellAsset` | `Asset` | ETH on Ethereum | Initial asset to sell. | | `defaultBuyAsset` | `Asset` | USDC on Ethereum | Initial asset to buy. | @@ -132,7 +130,7 @@ function App() { import { SwapWidget } from "@shapeshiftoss/swap-widget"; function App() { - return ; + return ; } ``` @@ -149,7 +147,6 @@ function App() { return ( { // Your custom wallet connection logic @@ -198,7 +195,6 @@ const defaultBuyAsset: Asset = { function App() { return ( @@ -481,10 +474,6 @@ The widget supports swaps across multiple blockchain types: - **Solana swaps** - Solana transactions can be signed via Phantom, Solflare, or other Solana wallets when using the built-in wallet connection. - **Unsupported chains** - Swaps involving chains without wallet support will redirect to [app.shapeshift.com](https://app.shapeshift.com) to complete the transaction. -### API Key - -An API key is required for fetching swap rates in production. Contact ShapeShift for API access. - ### Internal QueryClient The widget manages its own React Query `QueryClient` internally. You do not need to wrap it in a `QueryClientProvider`. diff --git a/packages/swap-widget/src/api/client.ts b/packages/swap-widget/src/api/client.ts index 473b96be29e..ed6349e388b 100644 --- a/packages/swap-widget/src/api/client.ts +++ b/packages/swap-widget/src/api/client.ts @@ -5,7 +5,6 @@ const DEFAULT_API_BASE_URL = export type ApiClientConfig = { baseUrl?: string - apiKey?: string } export const createApiClient = (config: ApiClientConfig = {}) => { @@ -21,9 +20,6 @@ export const createApiClient = (config: ApiClientConfig = {}) => { const headers: Record = { 'Content-Type': 'application/json', } - if (config.apiKey) { - headers['x-api-key'] = config.apiKey - } const controller = new AbortController() const timeoutId = setTimeout(() => controller.abort(), timeoutMs) diff --git a/packages/swap-widget/src/components/SwapWidget.tsx b/packages/swap-widget/src/components/SwapWidget.tsx index 3940b64de86..0f5331ddf2f 100644 --- a/packages/swap-widget/src/components/SwapWidget.tsx +++ b/packages/swap-widget/src/components/SwapWidget.tsx @@ -1284,8 +1284,8 @@ const SwapWidgetCore = ({ const SwapWidgetWithExternalWallet = (props: SwapWidgetProps) => { const apiClient = useMemo( - () => createApiClient({ baseUrl: props.apiBaseUrl, apiKey: props.apiKey }), - [props.apiBaseUrl, props.apiKey], + () => createApiClient({ baseUrl: props.apiBaseUrl }), + [props.apiBaseUrl], ) return ( @@ -1299,8 +1299,8 @@ const SwapWidgetWithInternalWallet = ( props: SwapWidgetProps & { walletConnectProjectId: string }, ) => { const apiClient = useMemo( - () => createApiClient({ baseUrl: props.apiBaseUrl, apiKey: props.apiKey }), - [props.apiBaseUrl, props.apiKey], + () => createApiClient({ baseUrl: props.apiBaseUrl }), + [props.apiBaseUrl], ) return ( diff --git a/packages/swap-widget/src/demo/App.tsx b/packages/swap-widget/src/demo/App.tsx index 56bf43a139f..2032e9d821a 100644 --- a/packages/swap-widget/src/demo/App.tsx +++ b/packages/swap-widget/src/demo/App.tsx @@ -375,7 +375,6 @@ const DemoContent = ({ theme, setTheme }: DemoContentProps) => {