diff --git a/.changeset/rotten-apes-sing.md b/.changeset/rotten-apes-sing.md new file mode 100644 index 00000000000..da132ce0a2e --- /dev/null +++ b/.changeset/rotten-apes-sing.md @@ -0,0 +1,5 @@ +--- +"thirdweb": minor +--- + +Remove `Bridge.routes` and `useBridgeRoutes` diff --git a/apps/portal/src/app/bridge/routes/page.mdx b/apps/portal/src/app/bridge/routes/page.mdx deleted file mode 100644 index cd9c2719f50..00000000000 --- a/apps/portal/src/app/bridge/routes/page.mdx +++ /dev/null @@ -1,172 +0,0 @@ -import { - Callout, - OpenSourceCard, - createMetadata, - InstallTabs, - SDKCard, - Grid, - ArticleIconCard, - Tabs, - TabsList, - TabsTrigger, - TabsContent, -} from "@doc"; -import { - ReactIcon, - TypeScriptIcon, - UnityIcon, - DotNetIcon, - UnrealEngineIcon, -} from "@/icons"; -import { ExternalLink } from "lucide-react"; - -export const metadata = createMetadata({ - image: { - title: "Find Routes", - icon: "payments", - }, - title: "Find Routes", - description: "Find available routes for bridging, swapping, and paying.", -}); - -# Find Routes - -Use thirdweb's `Bridge.routes` utility to find all routes between tokens. - - ### Get All Routes - - Retrieve all supported routes: - - ```typescript - import { Bridge } from "thirdweb"; - - const routes = await Bridge.routes({ - client, - }); - ``` - - This returns an array of route objects with information about supported token pairs: - - ```typescript - [ - { - destinationToken: { - address: "0x12c88a3C30A7AaBC1dd7f2c08a97145F5DCcD830", - chainId: 1, - decimals: 18, - iconUri: "https://assets.coingecko.com/coins/images/37207/standard/G.jpg", - name: "G7", - symbol: "G7", - }, - originToken: { - address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", - chainId: 480, - decimals: 18, - iconUri: "https://assets.relay.link/icons/1/light.png", - name: "Ether", - symbol: "ETH", - } - }, - // ... more routes - ] - ``` - - ### Filter Routes by Origin - - Get all routes starting from a specific chain and token: - - ```typescript - import { Bridge } from "thirdweb"; - - // Get all routes starting from mainnet ETH - const routes = await Bridge.routes({ - originChainId: 1, - originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", - client, - }); - ``` - - ### Filter Routes by Destination - - Find routes that end at a specific destination: - - ```typescript - import { Bridge } from "thirdweb"; - - // Get all routes ending at USDC on Optimism - const routes = await Bridge.routes({ - destinationChainId: 10, - destinationTokenAddress: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85", - client, - }); - ``` - - ### Filter Routes by Both Origin and Destination - - Find direct routes between specific token pairs: - - ```typescript - import { Bridge } from "thirdweb"; - - // Get routes from mainnet ETH to USDC on Optimism - const routes = await Bridge.routes({ - originChainId: 1, - originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", - destinationChainId: 10, - destinationTokenAddress: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85", - client, - }); - ``` - - ### Pagination - - For large result sets, use pagination to retrieve routes in chunks: - - ```typescript - import { Bridge } from "thirdweb"; - - // Get the first 10 routes starting from mainnet ETH - const routes = await Bridge.routes({ - originChainId: 1, - originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", - limit: 10, - offset: 0, - client, - }); - - // Get the next 10 routes - const nextRoutes = await Bridge.routes({ - originChainId: 1, - originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", - limit: 10, - offset: 10, - client, - }); - ``` - - ### Limit Route Complexity - - Control the maximum number of steps in the returned routes: - - ```typescript - import { Bridge } from "thirdweb"; - - // Get only direct routes (1 step) - const directRoutes = await Bridge.routes({ - originChainId: 1, - originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", - maxSteps: 1, - client, - }); - ``` - -## Going further - -To connect with other auth strategies, use external wallets, or sponsor gas for users, check out the following guides: - -- [Send a Payment](/payments/send) -- [Get Token Prices](/payments/prices) - -## Explore Full API References - -- [Bridge.routes](/references/typescript/v5/routes) diff --git a/apps/portal/src/app/bridge/sell/page.mdx b/apps/portal/src/app/bridge/sell/page.mdx index db85aafe47f..12153aad623 100644 --- a/apps/portal/src/app/bridge/sell/page.mdx +++ b/apps/portal/src/app/bridge/sell/page.mdx @@ -60,7 +60,6 @@ You can execute this quote exactly like a buy. See the [Send a Payment](/payment To connect with other auth strategies, use external wallets, or sponsor gas for users, check out the following guides: - [Send a Payment](/payments/send) -- [Get Routes](/payments/routes) ## Explore Full API References diff --git a/apps/portal/src/app/bridge/sidebar.tsx b/apps/portal/src/app/bridge/sidebar.tsx index 287f1ec677d..cbfc3ed4562 100644 --- a/apps/portal/src/app/bridge/sidebar.tsx +++ b/apps/portal/src/app/bridge/sidebar.tsx @@ -28,10 +28,6 @@ export const sidebar: SideBar = { href: `${bridgeSlug}/tokens`, name: "Get Token Prices", }, - { - href: `${bridgeSlug}/routes`, - name: "Get Routes", - }, { href: `${bridgeSlug}/bridge-widget-script`, name: "BridgeWidget Script", diff --git a/apps/portal/src/app/bridge/swap/page.mdx b/apps/portal/src/app/bridge/swap/page.mdx index 8d5afd6bbc1..b069558301e 100644 --- a/apps/portal/src/app/bridge/swap/page.mdx +++ b/apps/portal/src/app/bridge/swap/page.mdx @@ -255,23 +255,6 @@ export class SwapManager { throw new Error("Transaction timeout - please check status manually"); } - - async getAvailableRoutes(params: { - originChainId?: number; - originTokenAddress?: string; - destinationChainId?: number; - destinationTokenAddress?: string; - limit?: number; - }) { - try { - return await Bridge.routes({ - ...params, - client: this.client, - }); - } catch (error) { - throw new Error(`Failed to get routes: ${error.message}`); - } - } } ``` @@ -319,92 +302,6 @@ async function swapEthToMatic(amount: string, userAccount: Account) { } ``` -### Advanced Price Comparison - -Compare prices across multiple routes to find the best deal: - -```typescript -interface RouteComparison { - route: any; - quote: SwapQuote; - priceImpact: number; - gasEstimate: bigint; -} - -export class PriceComparator { - private swapManager: SwapManager; - - constructor(swapManager: SwapManager) { - this.swapManager = swapManager; - } - - async compareRoutes( - fromToken: { chainId: number; address: string }, - amount: bigint, - userAddress: string - ): Promise { - // Get available routes - const routes = await this.swapManager.getAvailableRoutes({ - originChainId: fromToken.chainId, - originTokenAddress: fromToken.address, - limit: 10, - }); - - // Get quotes for each route - const comparisons: RouteComparison[] = []; - - for (const route of routes) { - try { - const quote = await this.swapManager.getQuote({ - fromChainId: route.originToken.chainId, - fromTokenAddress: route.originToken.address, - toChainId: route.destinationToken.chainId, - toTokenAddress: route.destinationToken.address, - amount, - userAddress, - client: this.swapManager.client, - }); - - const priceImpact = this.calculatePriceImpact(quote, amount); - const gasEstimate = this.estimateGasCosts(quote); - - comparisons.push({ - route, - quote, - priceImpact, - gasEstimate, - }); - } catch (error) { - console.warn(`Failed to get quote for route ${route.originToken.symbol} -> ${route.destinationToken.symbol}:`, error); - } - } - - // Sort by best output amount - return comparisons.sort((a, b) => - Number(b.quote.destinationAmount) - Number(a.quote.destinationAmount) - ); - } - - private calculatePriceImpact(quote: SwapQuote, inputAmount: bigint): number { - // Simplified price impact calculation - const ratio = Number(quote.destinationAmount) / Number(quote.originAmount); - return Math.abs(1 - ratio) * 100; // Percentage - } - - private estimateGasCosts(quote: SwapQuote): bigint { - // Sum up gas estimates from all transactions - return quote.steps.reduce((total, step) => { - return total + step.transactions.reduce((stepTotal: bigint, tx: any) => { - return stepTotal + (tx.gasLimit || 200000n); // Default estimate - }, 0n); - }, 0n); - } -} -``` - - -**Gas Optimization Tip:** For frequently traded pairs, consider caching route data to reduce API calls and improve UI responsiveness. - @@ -415,20 +312,16 @@ export class PriceComparator { - **Cross-chain swaps** - Exchange tokens across 50+ supported chains - **Real-time quotes** - Get up-to-date pricing and execution estimates - **Route optimization** - Find the best path for any token pair -- **Price comparison** - Compare multiple routes to maximize output - **Status tracking** - Monitor cross-chain transaction progress - ## Going Further - [Send a Payment](/payments/send) - Learn about peer-to-peer transfers -- [Get Routes](/payments/routes) - Explore route discovery APIs -- [Token Prices](/payments/tokens) - Access real-time token pricing +- [Token Prices](/bridge/tokens) - Access real-time token pricing - [Webhooks](/payments/webhooks) - Get notified when swaps complete ## API Reference - [Bridge.Buy.prepare](/references/typescript/v5/buy/prepare) -- [Bridge.routes](/references/typescript/v5/routes) - [Bridge.status](/references/typescript/v5/status) \ No newline at end of file diff --git a/apps/portal/src/app/bridge/tokens/page.mdx b/apps/portal/src/app/bridge/tokens/page.mdx index b226b483bcd..dcdc18d5675 100644 --- a/apps/portal/src/app/bridge/tokens/page.mdx +++ b/apps/portal/src/app/bridge/tokens/page.mdx @@ -119,7 +119,6 @@ const nextTokens = await Bridge.tokens({ To connect with other auth strategies, use external wallets, or sponsor gas for users, check out the following guides: - [Send a Payment](/payments/send) -- [Get Routes](/payments/routes) ## Explore Full API References diff --git a/apps/portal/src/app/payments/send/page.mdx b/apps/portal/src/app/payments/send/page.mdx index bba9855269c..850ebdc3262 100644 --- a/apps/portal/src/app/payments/send/page.mdx +++ b/apps/portal/src/app/payments/send/page.mdx @@ -264,7 +264,6 @@ Once this code completes, your payment has been fully executed. Payments normall - [Sell a Product](/payments/products) - [Token Prices](/payments/tokens) -- [Routes](/payments/routes) ## Explore API References diff --git a/apps/portal/src/app/payments/sidebar.tsx b/apps/portal/src/app/payments/sidebar.tsx index a0e5a6b1ce5..d0a207502b5 100644 --- a/apps/portal/src/app/payments/sidebar.tsx +++ b/apps/portal/src/app/payments/sidebar.tsx @@ -49,10 +49,6 @@ export const sidebar: SideBar = { href: `/bridge/tokens`, name: "Get Token Prices", }, - { - href: `/bridge/routes`, - name: "Get Routes", - }, { href: `${paymentsSlug}/webhooks`, name: "Webhooks", diff --git a/apps/portal/src/app/payments/webhooks/page.mdx b/apps/portal/src/app/payments/webhooks/page.mdx index 03e7a8cb538..e494966e005 100644 --- a/apps/portal/src/app/payments/webhooks/page.mdx +++ b/apps/portal/src/app/payments/webhooks/page.mdx @@ -222,7 +222,6 @@ async function verifyWebhook( To connect with other auth strategies, use external wallets, or sponsor gas for users, check out the following guides: - [Send a Payment](/payments/send) -- [Find Routes](/payments/routes) - [Get Token Prices](/payments/tokens) ## Explore Full API References diff --git a/packages/thirdweb/src/bridge/Routes.test.ts b/packages/thirdweb/src/bridge/Routes.test.ts deleted file mode 100644 index 0b0ce4b4335..00000000000 --- a/packages/thirdweb/src/bridge/Routes.test.ts +++ /dev/null @@ -1,161 +0,0 @@ -import { http, passthrough } from "msw"; -import { setupServer } from "msw/node"; -import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest"; -import { TEST_CLIENT } from "~test/test-clients.js"; -import { routes } from "./Routes.js"; - -const server = setupServer( - http.get("https://bridge.thirdweb.com/v1/routes", () => { - passthrough(); - }), -); - -describe.runIf(process.env.TW_SECRET_KEY)("Bridge.routes", () => { - beforeAll(() => server.listen()); - afterEach(() => server.resetHandlers()); - afterAll(() => server.close()); - - it("should get a valid list of routes", async () => { - const allRoutes = await routes({ - client: TEST_CLIENT, - }); - - expect(allRoutes).toBeDefined(); - expect(Array.isArray(allRoutes)).toBe(true); - }); - - it("should filter routes by origin chain", async () => { - const filteredRoutes = await routes({ - client: TEST_CLIENT, - originChainId: 1, - }); - - expect(filteredRoutes).toBeDefined(); - expect(Array.isArray(filteredRoutes)).toBe(true); - expect( - filteredRoutes.every((route) => route.originToken.chainId === 1), - ).toBe(true); - }); - - it("should filter routes by destination chain", async () => { - const filteredRoutes = await routes({ - client: TEST_CLIENT, - destinationChainId: 1, - }); - - expect(filteredRoutes).toBeDefined(); - expect(Array.isArray(filteredRoutes)).toBe(true); - expect( - filteredRoutes.every((route) => route.destinationToken.chainId === 1), - ).toBe(true); - }); - - it("should filter routes by origin token", async () => { - const filteredRoutes = await routes({ - client: TEST_CLIENT, - originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", - }); - - expect(filteredRoutes).toBeDefined(); - expect(Array.isArray(filteredRoutes)).toBe(true); - expect( - filteredRoutes.every( - (route) => - route.originToken.address === - "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", - ), - ).toBe(true); - }); - - it("should filter routes by destination token", async () => { - const filteredRoutes = await routes({ - client: TEST_CLIENT, - destinationTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", - }); - - expect(filteredRoutes).toBeDefined(); - expect(Array.isArray(filteredRoutes)).toBe(true); - expect( - filteredRoutes.every( - (route) => - route.destinationToken.address === - "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", - ), - ).toBe(true); - }); - - it("should combine filters", async () => { - const filteredRoutes = await routes({ - client: TEST_CLIENT, - destinationChainId: 10, - destinationTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", - originChainId: 1, - originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", - }); - - expect(filteredRoutes).toBeDefined(); - expect(Array.isArray(filteredRoutes)).toBe(true); - expect(filteredRoutes.length).toBeGreaterThan(0); - expect( - filteredRoutes.every( - (route) => - route.originToken.chainId === 1 && - route.destinationToken.chainId === 10 && - route.originToken.address === - "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" && - route.destinationToken.address === - "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", - ), - ).toBe(true); - }); - - it("should respect limit and offset", async () => { - const page1Routes = await routes({ - client: TEST_CLIENT, - limit: 1, - offset: 1, - }); - - expect(page1Routes).toBeDefined(); - expect(Array.isArray(page1Routes)).toBe(true); - expect(page1Routes.length).toBe(1); - - const page2Routes = await routes({ - client: TEST_CLIENT, - limit: 1, - offset: 2, - }); - - expect(page2Routes).toBeDefined(); - expect(Array.isArray(page2Routes)).toBe(true); - expect(page2Routes.length).toBe(1); - - expect(JSON.stringify(page1Routes)).not.toEqual( - JSON.stringify(page2Routes), - ); - }); - - it("should surface any errors", async () => { - server.use( - http.get("https://bridge.thirdweb.com/v1/routes", () => { - return Response.json( - { - code: "InvalidRoutesRequest", - message: "The provided request is invalid.", - }, - { status: 400 }, - ); - }), - ); - - await expect( - routes({ - client: TEST_CLIENT, - limit: 1000, - offset: 1000, - }), - ).rejects.toThrowErrorMatchingInlineSnapshot( - "[Error: The provided request is invalid.]", - ); - }); -}); diff --git a/packages/thirdweb/src/bridge/Routes.ts b/packages/thirdweb/src/bridge/Routes.ts deleted file mode 100644 index 66ee028c101..00000000000 --- a/packages/thirdweb/src/bridge/Routes.ts +++ /dev/null @@ -1,228 +0,0 @@ -import type { Address as ox__Address, Hex as ox__Hex } from "ox"; -import type { ThirdwebClient } from "../client/client.js"; -import { getThirdwebBaseUrl } from "../utils/domains.js"; -import { getClientFetch } from "../utils/fetch.js"; -import { ApiError } from "./types/Errors.js"; -import type { Route } from "./types/Route.js"; - -/** - * Retrieves supported Bridge routes based on the provided filters. - * - * When multiple filters are specified, a route must satisfy all filters to be included (it acts as an AND operator). - * - * @example - * ```typescript - * import { Bridge } from "thirdweb"; - * - * const routes = await Bridge.routes({ - * client: thirdwebClient, - * }); - * ``` - * - * Returned routes might look something like: - * ```typescript - * [ - * { - * destinationToken: { - * address: "0x12c88a3C30A7AaBC1dd7f2c08a97145F5DCcD830", - * chainId: 1, - * decimals: 18, - * iconUri: "https://assets.coingecko.com/coins/images/37207/standard/G.jpg", - * name: "G7", - * symbol: "G7", - * }, - * originToken: { - * address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", - * chainId: 480, - * decimals: 18, - * iconUri: "https://assets.relay.link/icons/1/light.png", - * name: "Ether", - * symbol: "ETH", - * } - * }, - * { - * destinationToken: { - * address: "0x4d224452801ACEd8B2F0aebE155379bb5D594381", - * chainId: 1, - * decimals: 18, - * iconUri: "https://coin-images.coingecko.com/coins/images/24383/large/apecoin.jpg?1696523566", - * name: "ApeCoin", - * symbol: "APE", - * }, - * originToken: { - * address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", - * chainId: 480, - * decimals: 18, - * iconUri: "https://assets.relay.link/icons/1/light.png", - * name: "Ether", - * symbol: "ETH", - * } - * } - * ] - * ``` - * - * You can filter for specific chains or tokens: - * ```typescript - * import { Bridge } from "thirdweb"; - * - * // Get all routes starting from mainnet ETH - * const routes = await Bridge.routes({ - * originChainId: 1, - * originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", - * client: thirdwebClient, - * }); - * ``` - * - * The returned routes will be limited based on the API. You can paginate through the results using the `limit` and `offset` parameters: - * ```typescript - * import { Bridge } from "thirdweb"; - * - * // Get the first 10 routes starting from mainnet ETH - * const routes = await Bridge.routes({ - * originChainId: 1, - * originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", - * limit: 10, - * offset: 0, - * client: thirdwebClient, - * }); - * ``` - * - * You can sort the returned routes by `popularity`: - * ```ts - * import { Bridge } from "thirdweb"; - * - * // Get the 10 most popular routes starting from mainnet ETH - * const routes = await Bridge.routes({ - * originChainId: 1, - * originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", - * limit: 10, - * offset: 0, - * sortBy: "popularity", - * client: thirdwebClient, - * }); - * ``` - * - * @param options - The options for the quote. - * @param options.client - Your thirdweb client. - * @param options.originChainId - Filter by a specific origin chain ID. - * @param options.originTokenAddress - Filter by a specific origin token address. - * @param options.destinationChainId - Filter by a specific destination chain ID. - * @param options.destinationTokenAddress - Filter by a specific destination token address. - * @param options.transactionHash - Filter by a specific transaction hash. - * @param options.maxSteps - Limit the number of steps returned. - * @param options.sortBy - Sort the routes by various categories. - * @param options.limit - Limit the number of routes returned. - * @param options.offset - Offset the number of routes returned. - * - * @returns A promise that resolves to an array of routes. - * - * @throws Will throw an error if there is an issue fetching the routes. - * @bridge - * @beta - */ -export async function routes(options: routes.Options): Promise { - const { - client, - originChainId, - originTokenAddress, - destinationChainId, - destinationTokenAddress, - maxSteps, - sortBy, - limit, - offset, - includePrices, - } = options; - - const clientFetch = getClientFetch(client); - const url = new URL(`${getThirdwebBaseUrl("bridge")}/v1/routes`); - if (originChainId) { - url.searchParams.set("originChainId", originChainId.toString()); - } - if (originTokenAddress) { - url.searchParams.set("originTokenAddress", originTokenAddress); - } - if (destinationChainId) { - url.searchParams.set("destinationChainId", destinationChainId.toString()); - } - if (destinationTokenAddress) { - url.searchParams.set("destinationTokenAddress", destinationTokenAddress); - } - if (maxSteps) { - url.searchParams.set("maxSteps", maxSteps.toString()); - } - if (limit) { - url.searchParams.set("limit", limit.toString()); - } - if (offset) { - url.searchParams.set("offset", offset.toString()); - } - if (sortBy) { - url.searchParams.set("sortBy", sortBy); - } - if (includePrices) { - url.searchParams.set("includePrices", includePrices.toString()); - } - - const response = await clientFetch(url.toString()); - if (!response.ok) { - const errorJson = await response.json(); - throw new ApiError({ - code: errorJson.code || "UNKNOWN_ERROR", - correlationId: errorJson.correlationId || undefined, - message: errorJson.message || response.statusText, - statusCode: response.status, - }); - } - - const { data }: { data: Route[] } = await response.json(); - return data; -} - -/** - * Namespace containing types for the routes function. - * @namespace routes - * @bridge - */ -export declare namespace routes { - /** - * Options for fetching available bridge routes. - * @interface Options - * @bridge - */ - type Options = { - /** Your thirdweb client */ - client: ThirdwebClient; - /** The origin chain ID to filter routes by */ - originChainId?: number; - /** The origin token address to filter routes by */ - originTokenAddress?: ox__Address.Address; - /** The destination chain ID to filter routes by */ - destinationChainId?: number; - /** The destination token address to filter routes by */ - destinationTokenAddress?: ox__Address.Address; - /** Transaction hash to filter routes by */ - transactionHash?: ox__Hex.Hex; - /** - * Sort routes by popularity - * @deprecated - */ - sortBy?: "popularity"; - /** Maximum number of steps in the route */ - maxSteps?: number; - /** Whether to include price information in the response */ - includePrices?: boolean; - /** Number of results to return (pagination) */ - limit?: number; - /** Number of results to skip (pagination) */ - offset?: number; - }; - - /** - * Result returned from fetching bridge routes. - * Contains an array of available routes. - * @interface Result - * @bridge - */ - type Result = Route[]; -} diff --git a/packages/thirdweb/src/bridge/index.ts b/packages/thirdweb/src/bridge/index.ts index 54a1958b365..502f9123454 100644 --- a/packages/thirdweb/src/bridge/index.ts +++ b/packages/thirdweb/src/bridge/index.ts @@ -1,7 +1,6 @@ export * as Buy from "./Buy.js"; export { chains } from "./Chains.js"; export * as Onramp from "./Onramp.js"; -export { routes } from "./Routes.js"; export * as Sell from "./Sell.js"; export { status } from "./Status.js"; export { tokens } from "./Token.js"; diff --git a/packages/thirdweb/src/exports/react.ts b/packages/thirdweb/src/exports/react.ts index 7c3a89bd16d..337fd0d60f8 100644 --- a/packages/thirdweb/src/exports/react.ts +++ b/packages/thirdweb/src/exports/react.ts @@ -74,10 +74,6 @@ export type { } from "../react/core/hooks/transaction/useSendTransaction.js"; export { useSimulateTransaction } from "../react/core/hooks/transaction/useSimulateTransaction.js"; export type { BridgePrepareResult } from "../react/core/hooks/useBridgePrepare.js"; -export { - type UseBridgeRoutesParams, - useBridgeRoutes, -} from "../react/core/hooks/useBridgeRoutes.js"; export type { CompletedStatusResult } from "../react/core/hooks/useStepExecutor.js"; export { useActiveAccount } from "../react/core/hooks/wallets/useActiveAccount.js"; // wallet hooks diff --git a/packages/thirdweb/src/react/core/hooks/transaction/useSendTransaction.ts b/packages/thirdweb/src/react/core/hooks/transaction/useSendTransaction.ts index 52fdfa933fe..13c24488ec0 100644 --- a/packages/thirdweb/src/react/core/hooks/transaction/useSendTransaction.ts +++ b/packages/thirdweb/src/react/core/hooks/transaction/useSendTransaction.ts @@ -253,10 +253,10 @@ export function useSendTransactionCore(args: { (nativeCost > 0n && nativeBalance.value < nativeCost); if (shouldShowModal) { - const supportedDestinations = await Bridge.routes({ + const tokens = await Bridge.tokens({ client: tx.client, - destinationChainId: tx.chain.id, - destinationTokenAddress: _erc20Value?.tokenAddress, + chainId: tx.chain.id, + tokenAddress: _erc20Value?.tokenAddress, }).catch((err) => { trackPayEvent({ client: tx.client, @@ -269,11 +269,8 @@ export function useSendTransactionCore(args: { return null; }); - if ( - !supportedDestinations || - supportedDestinations.length === 0 - ) { - // not a supported destination -> show deposit screen + if (!tokens || tokens.length === 0) { + // not a supported token -> show deposit screen trackPayEvent({ client: tx.client, error: JSON.stringify({ diff --git a/packages/thirdweb/src/react/core/hooks/useBridgeRoutes.test.ts b/packages/thirdweb/src/react/core/hooks/useBridgeRoutes.test.ts deleted file mode 100644 index 9839a935d7b..00000000000 --- a/packages/thirdweb/src/react/core/hooks/useBridgeRoutes.test.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { - beforeEach, - describe, - expect, - it, - type MockedFunction, - vi, -} from "vitest"; -import { routes } from "../../../bridge/Routes.js"; -import { ApiError } from "../../../bridge/types/Errors.js"; -import type { Route } from "../../../bridge/types/Route.js"; -import type { ThirdwebClient } from "../../../client/client.js"; -import type { UseBridgeRoutesParams } from "./useBridgeRoutes.js"; - -// Mock the Bridge routes function -vi.mock("../../../bridge/Routes.js", () => ({ - routes: vi.fn(), -})); - -const mockRoutes = routes as MockedFunction; - -// Mock client -const mockClient = { clientId: "test" } as ThirdwebClient; - -// Mock route data -const mockRouteData: Route[] = [ - { - destinationToken: { - address: "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", - chainId: 137, - decimals: 18, - name: "Wrapped Ethereum", - priceUsd: 2000.0, - symbol: "WETH", - }, - originToken: { - address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", - chainId: 1, - decimals: 18, - name: "Ethereum", - priceUsd: 2000.0, - symbol: "ETH", - }, - }, -]; - -describe("useBridgeRoutes", () => { - beforeEach(() => { - vi.clearAllMocks(); - }); - - it("should export correct hook parameters type", () => { - // Type-only test to verify UseBridgeRoutesParams interface - const params: UseBridgeRoutesParams = { - client: mockClient, - destinationChainId: 137, - enabled: true, - originChainId: 1, - }; - - expect(params).toBeDefined(); - expect(params.client).toBe(mockClient); - expect(params.originChainId).toBe(1); - expect(params.destinationChainId).toBe(137); - expect(params.enabled).toBe(true); - }); - - it("should handle different parameter combinations", () => { - const fullParams: UseBridgeRoutesParams = { - client: mockClient, - destinationChainId: 137, - destinationTokenAddress: "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", - enabled: false, - limit: 10, - maxSteps: 3, - offset: 0, - originChainId: 1, - originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", - sortBy: "popularity", - }; - - expect(fullParams).toBeDefined(); - expect(fullParams.sortBy).toBe("popularity"); - expect(fullParams.maxSteps).toBe(3); - expect(fullParams.limit).toBe(10); - expect(fullParams.offset).toBe(0); - }); - - it("should have optional enabled parameter defaulting to true", () => { - const paramsWithoutEnabled: UseBridgeRoutesParams = { - client: mockClient, - destinationChainId: 137, - originChainId: 1, - }; - - expect(paramsWithoutEnabled.enabled).toBeUndefined(); // Should be optional - }); - - it("should validate that Bridge.routes would be called with correct parameters", async () => { - const testParams = { - client: mockClient, - destinationChainId: 137, - originChainId: 1, - originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" as const, - }; - - // Mock the routes function to return our test data - mockRoutes.mockResolvedValue(mockRouteData); - - // Directly call the routes function to verify it works with our parameters - const result = await routes(testParams); - - expect(mockRoutes).toHaveBeenCalledWith(testParams); - expect(result).toEqual(mockRouteData); - }); - - it("should handle API errors properly", async () => { - const apiError = new ApiError({ - code: "INVALID_INPUT", - message: "Invalid parameters", - statusCode: 400, - }); - - mockRoutes.mockRejectedValue(apiError); - - try { - await routes({ - client: mockClient, - destinationChainId: 137, - originChainId: 1, - }); - } catch (error) { - expect(error).toBe(apiError); - expect(error).toBeInstanceOf(ApiError); - } - }); -}); diff --git a/packages/thirdweb/src/react/core/hooks/useBridgeRoutes.ts b/packages/thirdweb/src/react/core/hooks/useBridgeRoutes.ts deleted file mode 100644 index f9f4ddbb14b..00000000000 --- a/packages/thirdweb/src/react/core/hooks/useBridgeRoutes.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { useQuery } from "@tanstack/react-query"; -import type { routes as RoutesTypes } from "../../../bridge/Routes.js"; -import { routes } from "../../../bridge/Routes.js"; -import { ApiError } from "../../../bridge/types/Errors.js"; -import { mapBridgeError } from "../errors/mapBridgeError.js"; - -/** - * Parameters for the useBridgeRoutes hook - */ -export type UseBridgeRoutesParams = RoutesTypes.Options & { - /** - * Whether to enable the query. Useful for conditional fetching. - * @default true - */ - enabled?: boolean; -}; - -/** - * Hook that fetches available bridge routes with caching and retry logic - * - * @param params - Parameters for fetching routes including client and filter options - * @returns React Query result with routes data, loading state, and error handling - * - * @example - * ```tsx - * const { data: routes, isLoading, error } = useBridgeRoutes({ - * client: thirdwebClient, - * originChainId: 1, - * destinationChainId: 137, - * originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" - * }); - * ``` - */ -export function useBridgeRoutes(params: UseBridgeRoutesParams) { - const { enabled = true, ...routeParams } = params; - - return useQuery({ - enabled: enabled && !!routeParams.client, - gcTime: 10 * 60 * 1000, - queryFn: () => routes(routeParams), - queryKey: [ - "bridge-routes", - { - destinationChainId: routeParams.destinationChainId, - destinationTokenAddress: routeParams.destinationTokenAddress, - limit: routeParams.limit, - maxSteps: routeParams.maxSteps, - offset: routeParams.offset, - originChainId: routeParams.originChainId, - originTokenAddress: routeParams.originTokenAddress, - sortBy: routeParams.sortBy, - }, - ], // 5 minutes - routes are relatively stable - retry: (failureCount, error) => { - // Handle both ApiError and generic Error instances - if (error instanceof ApiError) { - const bridgeError = mapBridgeError(error); - - // Don't retry on client-side errors (4xx) - if ( - bridgeError.statusCode && - bridgeError.statusCode >= 400 && - bridgeError.statusCode < 500 - ) { - return false; - } - } - - // Retry up to 3 times for server errors or network issues - return failureCount < 3; - }, // 10 minutes garbage collection - retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000), - staleTime: 5 * 60 * 1000, // Exponential backoff, max 30s - }); -}