import {z} from 'zod' export type GetApiV10ChainEstimationArgs = {chainId: '1' | '10' | '56' | '137' | '8453' | '42161' | '43114' | '59144' | '7565164' | '100000001' | '100000002' | '100000008' | '100000009' | '100000013' | '100000014' | '100000017' | '100000019' | '100000020' | '100000021' | '100000022' | '100000023' | '100000025' | '100000026' | '100000027' | '100000028' | '100000029' | '100000030' | '100000031', tokenIn: string, tokenInAmount: string, slippage?: string | undefined, tokenOut: string, tokenOutAmount?: string | undefined, affiliateFeePercent?: number | undefined, affiliateFeeRecipient?: string | undefined}; export type GetApiV10ChainEstimationOptions = {baseUrl?: string | undefined, fetch?: typeof fetch | undefined}; export const tokenWithUsdValue = z.object({address: z.string(), name: z.string(), symbol: z.string(), decimals: z.number().int(), amount: z.string(), approximateUsdValue: z.number().optional()}); export const tokenWithMinAmount = z.object({address: z.string(), name: z.string(), symbol: z.string(), decimals: z.number().int(), amount: z.string(), minAmount: z.string(), approximateUsdValue: z.number().optional()}); export const feeDetails = z.object({giveOrderState: z.string().optional(), giveOrderWallet: z.string().optional(), nonceMaster: z.string().optional(), txFee: z.string().optional(), priorityFee: z.string().optional(), gasLimit: z.string().optional(), gasPrice: z.string().optional(), baseFee: z.string().optional(), maxFeePerGas: z.string().optional(), maxPriorityFeePerGas: z.string().optional()}); export const feeInfo = z.object({total: z.string(), details: feeDetails, approximateUsdValue: z.number().optional()}); export const payload = z.object({feeAmount: z.string().optional(), feeBps: z.string().optional(), amountOutBeforeCorrection: z.string().optional(), estimatedVolatilityBps: z.string().optional(), actualFeeAmount: z.string().optional(), actualFeeBps: z.string().optional(), subsidyAmount: z.string().optional(), feeApproximateUsdValue: z.string().optional(), additionalMarginAmount: z.string().optional(), additionalMarginBps: z.string().optional()}); export const costsDetailsProperty = z.object({chain: z.string(), tokenIn: z.string(), tokenOut: z.string(), amountIn: z.string(), amountOut: z.string(), type: z.string(), payload: payload.optional()}); export const swapAdapterComparisonDto = z.object({name: z.string(), amount: z.string(), approximateUsdValue: z.number(), priceDrop: z.number(), imageUrl: z.string()}); export const singleChainEstimation = z.object({tokenIn: tokenWithUsdValue, tokenOut: tokenWithMinAmount, slippage: z.number(), recommendedSlippage: z.number(), protocolFee: z.string().optional(), protocolFeeApproximateUsdValue: z.string().optional(), estimatedTransactionFee: feeInfo.optional(), costsDetails: z.array(costsDetailsProperty).optional(), comparedAggregators: z.array(swapAdapterComparisonDto)}); export const chainEstimationResponseV10 = z.object({estimation: singleChainEstimation}); export const validateResponse = async ( schema: { readonly '~standard': { readonly validate: (value: unknown) => ({ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }) | Promise<{ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }> } }, value: unknown ): Promise => { const result = await schema['~standard'].validate(value) if (result.issues !== undefined) { const detail = result.issues .map(({ message, path }) => path?.length ? `${path.map((segment) => String(typeof segment === 'object' ? segment.key : segment)).join('.')}: ${message}` : message ) .join('; ') throw new Error(`Response does not match the schema: ${detail}`, { cause: { issues: result.issues } }) } return result.value }; /** * GET /v1.0/chain/estimation */ export const getApiV10ChainEstimation = async (args: GetApiV10ChainEstimationArgs, options?: GetApiV10ChainEstimationOptions) => { const {baseUrl = "https://dln.debridge.finance", fetch: fetchFn = fetch} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('accept', "application/json") if (args.chainId != null) query.append("chainId", String(args.chainId)) if (args.tokenIn != null) query.append("tokenIn", String(args.tokenIn)) if (args.tokenInAmount != null) query.append("tokenInAmount", String(args.tokenInAmount)) if (args.slippage != null) query.append("slippage", String(args.slippage)) if (args.tokenOut != null) query.append("tokenOut", String(args.tokenOut)) if (args.tokenOutAmount != null) query.append("tokenOutAmount", String(args.tokenOutAmount)) if (args.affiliateFeePercent != null) query.append("affiliateFeePercent", String(args.affiliateFeePercent)) if (args.affiliateFeeRecipient != null) query.append("affiliateFeeRecipient", String(args.affiliateFeeRecipient)) const url = `${baseUrl.replace(/\/+$/, '')}/v1.0/chain/estimation${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "GET", headers}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(chainEstimationResponseV10, await res.json()) };