Skip to content

Commit bb940b8

Browse files
committed
add /swaps
1 parent 22532e5 commit bb940b8

File tree

11 files changed

+219
-174
lines changed

11 files changed

+219
-174
lines changed

src/api/routes/swap/swapModel.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { z } from "zod"
66
extendZodWithOpenApi(z)
77

88
export type Meta = z.infer<typeof metaSchema>
9+
export type SwapResponseSingle = z.infer<typeof swapResponseSchemaSingle>
910
export type SwapResponse = z.infer<typeof swapResponseSchema>
1011

1112
const addressSchema = z
@@ -265,7 +266,7 @@ const getSwapSchema = z.object({
265266
}),
266267
})
267268

268-
const swapResponseSchema = z.object({
269+
const swapResponseSchemaSingle = z.object({
269270
amountIn: z.string().openapi({
270271
description:
271272
"In exact output - the trade quote. In exact input - the exact sold amount",
@@ -320,4 +321,6 @@ const swapResponseSchema = z.object({
320321
.openapi({ description: "Swap route details" }),
321322
})
322323

323-
export { getSwapSchema, swapResponseSchema }
324+
const swapResponseSchema = z.array(swapResponseSchemaSingle)
325+
326+
export { getSwapSchema, swapResponseSchemaSingle, swapResponseSchema }

src/api/routes/swap/swapRouter.ts

Lines changed: 56 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -8,88 +8,90 @@ import {
88
handleServiceResponse,
99
validateRequest,
1010
} from "@/common/utils/httpHandlers"
11-
import { runPipeline } from "@/swapService/runner"
11+
import { findSwaps } from "@/swapService/runner"
1212
import type { SwapParams } from "@/swapService/types"
13-
import {
14-
ApiError,
15-
addInOutDeposits,
16-
findToken,
17-
getSwapper,
18-
} from "@/swapService/utils"
13+
import { ApiError, findToken, getSwapper } from "@/swapService/utils"
1914
import { StatusCodes } from "http-status-codes"
20-
import { InvalidAddressError, isHex } from "viem"
15+
import { InvalidAddressError } from "viem"
2116
import { z } from "zod"
2217
import {
2318
type SwapResponse,
19+
type SwapResponseSingle,
2420
getSwapSchema,
2521
swapResponseSchema,
22+
swapResponseSchemaSingle,
2623
} from "./swapModel"
2724

2825
export const swapRegistry = new OpenAPIRegistry()
2926
export const swapRouter: Router = express.Router()
3027

31-
swapRegistry.register("SwapQuote", swapResponseSchema)
28+
swapRegistry.register("SwapQuote", swapResponseSchemaSingle)
3229
swapRegistry.registerPath({
3330
method: "get",
3431
path: "/swap",
35-
tags: ["Get swap quote"],
32+
tags: ["Get the best swap quote"],
33+
request: { query: getSwapSchema.shape.query },
34+
responses: createApiResponse(swapResponseSchemaSingle, "Success"),
35+
})
36+
37+
swapRegistry.register("SwapQuotes", swapResponseSchema)
38+
swapRegistry.registerPath({
39+
method: "get",
40+
path: "/swaps",
41+
tags: ["Get swap quotes ordered from best to worst"],
3642
request: { query: getSwapSchema.shape.query },
3743
responses: createApiResponse(swapResponseSchema, "Success"),
3844
})
3945

4046
swapRouter.get(
41-
"/",
47+
"/swap",
4248
validateRequest(getSwapSchema),
4349
async (req: Request, res: Response) => {
44-
const serviceResponse = await findSwap(req)
45-
console.log("===== END =====")
46-
return handleServiceResponse(serviceResponse, res)
50+
try {
51+
const swaps = await findSwaps(parseRequest(req))
52+
return handleServiceResponse(
53+
ServiceResponse.success<SwapResponseSingle>(swaps[0]),
54+
res,
55+
)
56+
} catch (error) {
57+
return handleServiceResponse(createFailureResponse(req, error), res)
58+
} finally {
59+
console.log("===== END =====")
60+
}
4761
},
4862
)
4963

50-
async function findSwap(
51-
req: Request,
52-
): Promise<ServiceResponse<SwapResponse | null>> {
53-
try {
54-
const swapParams = parseRequest(req)
55-
56-
let data = await runPipeline(swapParams)
57-
58-
// GLOBAL CHECKS
59-
data = addInOutDeposits(swapParams, data)
60-
61-
// make sure verify item includes at least a function selector
62-
if (
63-
!isHex(data.verify.verifierData) ||
64-
data.verify.verifierData.length < 10
65-
)
66-
throw new ApiError(
67-
StatusCodes.INTERNAL_SERVER_ERROR,
68-
"Verifier transaction is empty",
69-
)
70-
71-
return ServiceResponse.success<SwapResponse>(data)
72-
} catch (error) {
73-
console.log(
74-
"error: ",
75-
error.statusCode,
76-
error.message,
77-
error.errorMessage,
78-
JSON.stringify(error.data),
79-
req.url,
80-
)
81-
if (error instanceof ApiError) {
82-
return ServiceResponse.failure(
83-
error.message,
84-
error.statusCode,
85-
error.data,
64+
swapRouter.get(
65+
"/swaps",
66+
validateRequest(getSwapSchema),
67+
async (req: Request, res: Response) => {
68+
try {
69+
const swaps = await findSwaps(parseRequest(req))
70+
return handleServiceResponse(
71+
ServiceResponse.success<SwapResponse>(swaps),
72+
res,
8673
)
74+
} catch (error) {
75+
return handleServiceResponse(createFailureResponse(req, error), res)
76+
} finally {
77+
console.log("===== END =====")
8778
}
88-
return ServiceResponse.failure(
89-
`${error}`,
90-
StatusCodes.INTERNAL_SERVER_ERROR,
91-
)
79+
},
80+
)
81+
82+
function createFailureResponse(req: Request, error: any) {
83+
console.log(
84+
"error: ",
85+
error.statusCode,
86+
error.message,
87+
error.errorMessage,
88+
JSON.stringify(error.data),
89+
req.url,
90+
)
91+
if (error instanceof ApiError) {
92+
return ServiceResponse.failure(error.message, error.statusCode, error.data)
9293
}
94+
return ServiceResponse.failure(`${error}`, StatusCodes.INTERNAL_SERVER_ERROR)
9395
}
9496

9597
function parseRequest(request: Request): SwapParams {

src/server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ app.use(requestLogger)
2828

2929
// Routes
3030
app.use("/health-check", healthCheckRouter)
31-
app.use("/swap", swapRouter)
31+
app.use(swapRouter)
3232

3333
// Swagger UI
3434
app.use(openAPIRouter)

src/swapService/runner.ts

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import { SwapResponse } from "@/api/routes/swap/swapModel"
12
import { StatusCodes } from "http-status-codes"
3+
import { isHex } from "viem"
24
import { getRoutingConfig } from "./config"
35
import type {
46
ChainRoutingConfig,
@@ -7,7 +9,7 @@ import type {
79
} from "./interface"
810
import { strategies } from "./strategies/index"
911
import type { StrategyResult, SwapParams } from "./types"
10-
import { ApiError } from "./utils"
12+
import { ApiError, addInOutDeposits } from "./utils"
1113

1214
function loadPipeline(swapParams: SwapParams) {
1315
let routing: ChainRoutingConfig
@@ -32,14 +34,14 @@ function loadPipeline(swapParams: SwapParams) {
3234

3335
export async function runPipeline(
3436
swapParams: SwapParams,
35-
): Promise<SwapApiResponse> {
37+
): Promise<SwapApiResponse[]> {
3638
const pipeline = loadPipeline(swapParams)
3739

3840
const allResults: StrategyResult[] = []
3941
for (const strategy of pipeline) {
4042
const result = await strategy.findSwap(swapParams)
4143
allResults.push(result)
42-
if (result.response) break
44+
if (result.quotes) break
4345
}
4446

4547
console.log(allResults)
@@ -50,19 +52,43 @@ export async function runPipeline(
5052
StatusCodes.NOT_FOUND,
5153
"Pipeline empty or result not found",
5254
)
53-
if (!finalResult.response) {
55+
if (!finalResult.quotes || finalResult.quotes.length === 0) {
5456
throw new ApiError(
5557
StatusCodes.NOT_FOUND,
5658
"Swap quote not found",
5759
allResults,
5860
)
5961
}
6062

61-
// console.log(
62-
// "finalResult.response: ",
63-
// JSON.stringify(finalResult.response, null, 2),
64-
// )
65-
return finalResult.response
63+
console.log("Best quote", {
64+
amountIn: finalResult.quotes[0].amountIn,
65+
amountInMax: finalResult.quotes[0].amountInMax,
66+
amountOut: finalResult.quotes[0].amountOut,
67+
amountOutMin: finalResult.quotes[0].amountOutMin,
68+
route: finalResult.quotes[0].route,
69+
})
70+
71+
// console.log('finalResult.quotes: ', JSON.stringify(finalResult.quotes, null, 2));
72+
return finalResult.quotes
73+
}
74+
75+
export async function findSwaps(swapParams: SwapParams) {
76+
// GLOBAL CHECKS
77+
let quotes = await runPipeline(swapParams)
78+
79+
// make sure verify item includes at least a function selector
80+
quotes = quotes.filter(
81+
(q) => isHex(q.verify.verifierData) && q.verify.verifierData.length >= 10,
82+
)
83+
84+
if (quotes.length === 0)
85+
throw new ApiError(StatusCodes.INTERNAL_SERVER_ERROR, "Invalid quotes")
86+
87+
for (const quote of quotes) {
88+
addInOutDeposits(swapParams, quote)
89+
}
90+
91+
return quotes
6692
}
6793

6894
// TODO timeouts on balmy

0 commit comments

Comments
 (0)