Skip to content

Commit 00c60c0

Browse files
gregfromstlclaude
andcommitted
refactor: rename 'universal' to 'bridge' module
- Renamed `Universal` to `Bridge` throughout the codebase - Enhanced error handling with consistent error formatting - Added test coverage for error cases - Added comprehensive test suite for Routes functionality - Fixed a minor formatting issue in Quote.ts 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 827b3ee commit 00c60c0

File tree

18 files changed

+276
-178
lines changed

18 files changed

+276
-178
lines changed

packages/thirdweb/src/universal/Buy.test.ts renamed to packages/thirdweb/src/bridge/Buy.test.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { describe, expect, it } from "vitest";
33
import { TEST_CLIENT } from "~test/test-clients.js";
44
import * as Buy from "./Buy.js";
55

6-
describe("Universal.Buy.quote", () => {
6+
describe("Bridge.Buy.quote", () => {
77
it("should get a valid quote", async () => {
88
const quote = await Buy.quote({
99
originChainId: 1,
@@ -18,9 +18,24 @@ describe("Universal.Buy.quote", () => {
1818
expect(quote.destinationAmount).toEqual(toWei("0.01"));
1919
expect(quote.intent).toBeDefined();
2020
});
21+
22+
it("should surface any errors", async () => {
23+
await expect(
24+
Buy.quote({
25+
originChainId: 1,
26+
originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
27+
destinationChainId: 10,
28+
destinationTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
29+
buyAmountWei: toWei("1000000000"),
30+
client: TEST_CLIENT,
31+
}),
32+
).rejects.toThrowErrorMatchingInlineSnapshot(
33+
`[Error: AmountTooHigh | The provided amount is too high for the requested route.]`,
34+
);
35+
});
2136
});
2237

23-
describe("Universal.Buy.prepare", () => {
38+
describe("Bridge.Buy.prepare", () => {
2439
it("should get a valid prepared quote", async () => {
2540
const quote = await Buy.prepare({
2641
originChainId: 1,
@@ -39,4 +54,21 @@ describe("Universal.Buy.prepare", () => {
3954
expect(quote.transactions.length).toBeGreaterThan(0);
4055
expect(quote.intent).toBeDefined();
4156
});
57+
58+
it("should surface any errors", async () => {
59+
await expect(
60+
Buy.prepare({
61+
originChainId: 1,
62+
originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
63+
destinationChainId: 10,
64+
destinationTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
65+
buyAmountWei: toWei("1000000000"),
66+
sender: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
67+
receiver: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
68+
client: TEST_CLIENT,
69+
}),
70+
).rejects.toThrowErrorMatchingInlineSnapshot(
71+
`[Error: AmountTooHigh | The provided amount is too high for the requested route.]`,
72+
);
73+
});
4274
});

packages/thirdweb/src/universal/Buy.ts renamed to packages/thirdweb/src/bridge/Buy.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import type { PreparedQuote, Quote } from "./types/Quote.js";
99
*
1010
* @example
1111
* ```typescript
12-
* import { Universal } from "thirdweb";
12+
* import { Bridge } from "thirdweb";
1313
*
14-
* const quote = await Universal.Buy.quote({
14+
* const quote = await Bridge.Buy.quote({
1515
* originChainId: 1,
1616
* originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
1717
* destinationChainId: 10,
@@ -77,7 +77,7 @@ export async function quote(options: quote.Options): Promise<quote.Result> {
7777
const response = await clientFetch(url.toString());
7878
if (!response.ok) {
7979
const errorJson = await response.json();
80-
throw new Error(`${errorJson.code}: ${errorJson.message}`);
80+
throw new Error(`${errorJson.code} | ${errorJson.message}`);
8181
}
8282

8383
const { data }: { data: Quote } = await response.json();
@@ -123,9 +123,9 @@ export declare namespace quote {
123123
*
124124
* @example
125125
* ```typescript
126-
* import { Universal } from "thirdweb";
126+
* import { Bridge } from "thirdweb";
127127
*
128-
* const quote = await Universal.Buy.prepare({
128+
* const quote = await Bridge.Buy.prepare({
129129
* originChainId: 1,
130130
* originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
131131
* destinationChainId: 10,
@@ -169,7 +169,7 @@ export declare namespace quote {
169169
* - All transactions are assumed to be executed by the `sender` address, regardless of which chain they are on. The final transaction will use the `receiver` as the recipient address.
170170
* - If an `expiration` timestamp is provided, all transactions must be executed before that time to guarantee successful execution at the specified price.
171171
*
172-
* NOTE: To get the status of each transaction, use `Universal.status` rather than checking for transaction inclusion. This function will ensure full bridge completion on the destination chain.
172+
* NOTE: To get the status of each transaction, use `Bridge.status` rather than checking for transaction inclusion. This function will ensure full bridge completion on the destination chain.
173173
*
174174
* You can access this functions input and output types with `Buy.prepare.Options` and `Buy.prepare.Result`, respectively.
175175
*
@@ -214,7 +214,7 @@ export async function prepare(
214214
const response = await clientFetch(url.toString());
215215
if (!response.ok) {
216216
const errorJson = await response.json();
217-
throw new Error(`${errorJson.code}: ${errorJson.message}`);
217+
throw new Error(`${errorJson.code} | ${errorJson.message}`);
218218
}
219219

220220
const { data }: { data: PreparedQuote } = await response.json();
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import { afterAll, beforeAll, afterEach, describe, expect, it } from "vitest";
2+
import { TEST_CLIENT } from "~test/test-clients.js";
3+
import { routes } from "./Routes.js";
4+
import { http, passthrough } from "msw";
5+
import { setupServer } from "msw/node";
6+
7+
const server = setupServer(
8+
http.get("https://bridge.thirdweb.com/v1/routes", () => {
9+
passthrough();
10+
}),
11+
);
12+
13+
describe("Bridge.routes", () => {
14+
beforeAll(() => server.listen());
15+
afterEach(() => server.resetHandlers());
16+
afterAll(() => server.close());
17+
18+
it("should get a valid list of routes", async () => {
19+
const allRoutes = await routes({
20+
client: TEST_CLIENT,
21+
});
22+
23+
expect(allRoutes).toBeDefined();
24+
expect(Array.isArray(allRoutes)).toBe(true);
25+
});
26+
27+
it("should filter routes by origin chain", async () => {
28+
const filteredRoutes = await routes({
29+
client: TEST_CLIENT,
30+
originChainId: 1,
31+
});
32+
33+
expect(filteredRoutes).toBeDefined();
34+
expect(Array.isArray(filteredRoutes)).toBe(true);
35+
expect(
36+
filteredRoutes.every((route) => route.originToken.chainId === 1),
37+
).toBe(true);
38+
});
39+
40+
it("should filter routes by destination chain", async () => {
41+
const filteredRoutes = await routes({
42+
client: TEST_CLIENT,
43+
destinationChainId: 1,
44+
});
45+
46+
expect(filteredRoutes).toBeDefined();
47+
expect(Array.isArray(filteredRoutes)).toBe(true);
48+
expect(
49+
filteredRoutes.every((route) => route.destinationToken.chainId === 1),
50+
).toBe(true);
51+
});
52+
53+
it("should filter routes by origin token", async () => {
54+
const filteredRoutes = await routes({
55+
client: TEST_CLIENT,
56+
originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
57+
});
58+
59+
expect(filteredRoutes).toBeDefined();
60+
expect(Array.isArray(filteredRoutes)).toBe(true);
61+
expect(
62+
filteredRoutes.every(
63+
(route) =>
64+
route.originToken.address ===
65+
"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
66+
),
67+
).toBe(true);
68+
});
69+
70+
it("should filter routes by destination token", async () => {
71+
const filteredRoutes = await routes({
72+
client: TEST_CLIENT,
73+
destinationTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
74+
});
75+
76+
expect(filteredRoutes).toBeDefined();
77+
expect(Array.isArray(filteredRoutes)).toBe(true);
78+
expect(
79+
filteredRoutes.every(
80+
(route) =>
81+
route.destinationToken.address ===
82+
"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
83+
),
84+
).toBe(true);
85+
});
86+
87+
it("should combine filters", async () => {
88+
const filteredRoutes = await routes({
89+
client: TEST_CLIENT,
90+
originChainId: 1,
91+
destinationChainId: 10,
92+
originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
93+
destinationTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
94+
});
95+
96+
expect(filteredRoutes).toBeDefined();
97+
expect(Array.isArray(filteredRoutes)).toBe(true);
98+
expect(filteredRoutes.length).toBeGreaterThan(0);
99+
expect(
100+
filteredRoutes.every(
101+
(route) =>
102+
route.originToken.chainId === 1 &&
103+
route.destinationToken.chainId === 10 &&
104+
route.originToken.address ===
105+
"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" &&
106+
route.destinationToken.address ===
107+
"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
108+
),
109+
).toBe(true);
110+
});
111+
112+
it("should respect limit and offset", async () => {
113+
const page1Routes = await routes({
114+
client: TEST_CLIENT,
115+
limit: 1,
116+
offset: 1,
117+
});
118+
119+
expect(page1Routes).toBeDefined();
120+
expect(Array.isArray(page1Routes)).toBe(true);
121+
expect(page1Routes.length).toBe(1);
122+
123+
const page2Routes = await routes({
124+
client: TEST_CLIENT,
125+
limit: 1,
126+
offset: 2,
127+
});
128+
129+
expect(page2Routes).toBeDefined();
130+
expect(Array.isArray(page2Routes)).toBe(true);
131+
expect(page2Routes.length).toBe(1);
132+
133+
expect(JSON.stringify(page1Routes)).not.toEqual(
134+
JSON.stringify(page2Routes),
135+
);
136+
});
137+
138+
it("should surface any errors", async () => {
139+
server.use(
140+
http.get("https://bridge.thirdweb.com/v1/routes", () => {
141+
return Response.json(
142+
{
143+
code: "InvalidRoutesRequest",
144+
message: "The provided request is invalid.",
145+
},
146+
{ status: 400 },
147+
);
148+
}),
149+
);
150+
151+
await expect(
152+
routes({
153+
client: TEST_CLIENT,
154+
limit: 1000,
155+
offset: 1000,
156+
}),
157+
).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: InvalidRoutesRequest | The provided request is invalid.]`);
158+
});
159+
});

packages/thirdweb/src/universal/Routes.ts renamed to packages/thirdweb/src/bridge/Routes.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ import type { Route } from "./types/Route.js";
1111
*
1212
* @example
1313
* ```typescript
14-
* import { Universal } from "thirdweb";
14+
* import { Bridge } from "thirdweb";
1515
*
16-
* const routes = await Universal.routes({
16+
* const routes = await Bridge.routes({
1717
* client: thirdwebClient,
1818
* });
1919
* ```
@@ -62,10 +62,10 @@ import type { Route } from "./types/Route.js";
6262
*
6363
* You can filter for specific chains or tokens:
6464
* ```typescript
65-
* import { Universal } from "thirdweb";
65+
* import { Bridge } from "thirdweb";
6666
*
6767
* // Get all routes starting from mainnet ETH
68-
* const routes = await Universal.routes({
68+
* const routes = await Bridge.routes({
6969
* originChainId: 1,
7070
* originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
7171
* client: thirdwebClient,
@@ -74,10 +74,10 @@ import type { Route } from "./types/Route.js";
7474
*
7575
* The returned routes will be limited based on the API. You can paginate through the results using the `limit` and `offset` parameters:
7676
* ```typescript
77-
* import { Universal } from "thirdweb";
77+
* import { Bridge } from "thirdweb";
7878
*
7979
* // Get the first 10 routes starting from mainnet ETH
80-
* const routes = await Universal.routes({
80+
* const routes = await Bridge.routes({
8181
* originChainId: 1,
8282
* originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
8383
* limit: 10,
@@ -135,7 +135,7 @@ export async function routes(options: routes.Options): Promise<routes.Result> {
135135
const response = await clientFetch(url.toString());
136136
if (!response.ok) {
137137
const errorJson = await response.json();
138-
throw new Error(`${errorJson.code}: ${errorJson.message}`);
138+
throw new Error(`${errorJson.code} | ${errorJson.message}`);
139139
}
140140

141141
const { data }: { data: Route[] } = await response.json();

packages/thirdweb/src/universal/Sell.test.ts renamed to packages/thirdweb/src/bridge/Sell.test.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { describe, expect, it } from "vitest";
33
import { TEST_CLIENT } from "~test/test-clients.js";
44
import * as Sell from "./Sell.js";
55

6-
describe("Universal.Sell.quote", () => {
6+
describe("Bridge.Sell.quote", () => {
77
it("should get a valid quote", async () => {
88
const quote = await Sell.quote({
99
originChainId: 1,
@@ -18,9 +18,24 @@ describe("Universal.Sell.quote", () => {
1818
expect(quote.originAmount).toEqual(toWei("0.01"));
1919
expect(quote.intent).toBeDefined();
2020
});
21+
22+
it("should surface any errors", async () => {
23+
await expect(
24+
Sell.quote({
25+
originChainId: 1,
26+
originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
27+
destinationChainId: 10,
28+
destinationTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
29+
sellAmountWei: toWei("1000000000"),
30+
client: TEST_CLIENT,
31+
}),
32+
).rejects.toThrowErrorMatchingInlineSnapshot(
33+
`[Error: AmountTooHigh | The provided amount is too high for the requested route.]`,
34+
);
35+
});
2136
});
2237

23-
describe("Universal.Sell.prepare", () => {
38+
describe("Bridge.Sell.prepare", () => {
2439
it("should get a valid prepared quote", async () => {
2540
const quote = await Sell.prepare({
2641
originChainId: 1,
@@ -39,4 +54,21 @@ describe("Universal.Sell.prepare", () => {
3954
expect(quote.transactions.length).toBeGreaterThan(0);
4055
expect(quote.intent).toBeDefined();
4156
});
57+
58+
it("should surface any errors", async () => {
59+
await expect(
60+
Sell.prepare({
61+
originChainId: 1,
62+
originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
63+
destinationChainId: 10,
64+
destinationTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
65+
sellAmountWei: toWei("1000000000"),
66+
sender: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
67+
receiver: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
68+
client: TEST_CLIENT,
69+
}),
70+
).rejects.toThrowErrorMatchingInlineSnapshot(
71+
`[Error: AmountTooHigh | The provided amount is too high for the requested route.]`,
72+
);
73+
});
4274
});

0 commit comments

Comments
 (0)