diff --git a/packages/swap/src/adapters/dexhunter-api/api-maker.ts b/packages/swap/src/adapters/dexhunter-api/api-maker.ts index 3cd3cc6241..faa95498ad 100644 --- a/packages/swap/src/adapters/dexhunter-api/api-maker.ts +++ b/packages/swap/src/adapters/dexhunter-api/api-maker.ts @@ -1,7 +1,17 @@ import {FetchData, fetchData, isRight} from '@yoroi/common' import {Chain} from '@yoroi/types' import {freeze} from 'immer' -import {AveragePriceResponse, EstimateResponse, OrdersResponse} from './types' +import { + AveragePriceResponse, + CancelResponse, + EstimateResponse, + LimitOrderEstimate, + LimitOrderResponse, + OrdersResponse, + ReverseEstimateResponse, + SignResponse, + SwapResponse, +} from './types' import {transformers} from './transformers' import {DexhunterApi} from './dexhunter' @@ -92,6 +102,7 @@ export const dexhunterApiMaker = ({ true, ) }, + async orders(params: Parameters[0]) { const response = await request({ method: 'get', @@ -185,7 +196,309 @@ export const dexhunterApiMaker = ({ tag: 'left', error: { status: -3, - message: 'Failed to to estimate', + message: 'Failed to fetch estimate', + responseData: response.error.responseData, + }, + }, + true, + ) + }, + + async reverseEstimate( + body: Parameters[0], + ) { + const response = await request({ + method: 'post', + url: `${baseUrl}${apiPaths.estimate}`, + headers, + data: transformers.reverseEstimate.request(body), + }) + + if (isRight(response)) { + try { + const data = transformers.reverseEstimate.response( + response.value.data, + ) + + return freeze( + { + tag: 'right', + value: { + status: response.value.status, + data, + }, + }, + true, + ) + } catch (e) { + return freeze( + { + tag: 'left', + error: { + status: -3, + message: 'Failed to transform reverse estimate', + responseData: response.value.data, + }, + }, + true, + ) + } + } + return freeze( + { + tag: 'left', + error: { + status: -3, + message: 'Failed to fetch reverse estimate', + responseData: response.error.responseData, + }, + }, + true, + ) + }, + + async swap(body: Parameters[0]) { + const response = await request({ + method: 'post', + url: `${baseUrl}${apiPaths.swap}`, + headers, + data: transformers.swap.request(body), + }) + + if (isRight(response)) { + try { + const data = transformers.swap.response(response.value.data) + + return freeze( + { + tag: 'right', + value: { + status: response.value.status, + data, + }, + }, + true, + ) + } catch (e) { + return freeze( + { + tag: 'left', + error: { + status: -3, + message: 'Failed to transform swap', + responseData: response.value.data, + }, + }, + true, + ) + } + } + return freeze( + { + tag: 'left', + error: { + status: -3, + message: 'Failed to fetch swap', + responseData: response.error.responseData, + }, + }, + true, + ) + }, + + async sign(body: Parameters[0]) { + const response = await request({ + method: 'post', + url: `${baseUrl}${apiPaths.sign}`, + headers, + data: transformers.sign.request(body), + }) + + if (isRight(response)) { + try { + const data = transformers.sign.response(response.value.data) + + return freeze( + { + tag: 'right', + value: { + status: response.value.status, + data, + }, + }, + true, + ) + } catch (e) { + return freeze( + { + tag: 'left', + error: { + status: -3, + message: 'Failed to transform sign', + responseData: response.value.data, + }, + }, + true, + ) + } + } + return freeze( + { + tag: 'left', + error: { + status: -3, + message: 'Failed to fetch sign', + responseData: response.error.responseData, + }, + }, + true, + ) + }, + + async cancel(body: Parameters[0]) { + const response = await request({ + method: 'post', + url: `${baseUrl}${apiPaths.cancel}`, + headers, + data: transformers.cancel.request(body), + }) + + if (isRight(response)) { + try { + const data = transformers.cancel.response(response.value.data) + + return freeze( + { + tag: 'right', + value: { + status: response.value.status, + data, + }, + }, + true, + ) + } catch (e) { + return freeze( + { + tag: 'left', + error: { + status: -3, + message: 'Failed to transform cancel', + responseData: response.value.data, + }, + }, + true, + ) + } + } + return freeze( + { + tag: 'left', + error: { + status: -3, + message: 'Failed to fetch cancel', + responseData: response.error.responseData, + }, + }, + true, + ) + }, + + async limit(body: Parameters[0]) { + const response = await request({ + method: 'post', + url: `${baseUrl}${apiPaths.limit}`, + headers, + data: transformers.limit.request(body), + }) + + if (isRight(response)) { + try { + const data = transformers.limit.response(response.value.data) + + return freeze( + { + tag: 'right', + value: { + status: response.value.status, + data, + }, + }, + true, + ) + } catch (e) { + return freeze( + { + tag: 'left', + error: { + status: -3, + message: 'Failed to transform limit', + responseData: response.value.data, + }, + }, + true, + ) + } + } + return freeze( + { + tag: 'left', + error: { + status: -3, + message: 'Failed to fetch limit', + responseData: response.error.responseData, + }, + }, + true, + ) + }, + + async limitEstimate( + body: Parameters[0], + ) { + const response = await request({ + method: 'post', + url: `${baseUrl}${apiPaths.limitEstimate}`, + headers, + data: transformers.limitEstimate.request(body), + }) + + if (isRight(response)) { + try { + const data = transformers.limitEstimate.response( + response.value.data, + ) + + return freeze( + { + tag: 'right', + value: { + status: response.value.status, + data, + }, + }, + true, + ) + } catch (e) { + return freeze( + { + tag: 'left', + error: { + status: -3, + message: 'Failed to transform limit estimate', + responseData: response.value.data, + }, + }, + true, + ) + } + } + return freeze( + { + tag: 'left', + error: { + status: -3, + message: 'Failed to fetch limit estimate', responseData: response.error.responseData, }, }, diff --git a/packages/swap/src/adapters/dexhunter-api/dexhunter.ts b/packages/swap/src/adapters/dexhunter-api/dexhunter.ts index e7dd695124..856882c826 100644 --- a/packages/swap/src/adapters/dexhunter-api/dexhunter.ts +++ b/packages/swap/src/adapters/dexhunter-api/dexhunter.ts @@ -1,5 +1,14 @@ import {Api, Portfolio} from '@yoroi/types' -import {EstimateResponse, OrdersResponse} from './types' +import { + CancelResponse, + EstimateResponse, + LimitOrderEstimate, + LimitOrderResponse, + OrdersResponse, + ReverseEstimateResponse, + SignResponse, + SwapResponse, +} from './types' export type AveragePriceArgs = { tokenInId: Portfolio.Token.Id @@ -8,6 +17,11 @@ export type AveragePriceArgs = { export type OrdersArgs = {address: string} +export type CancelArgs = { + address: string + orderId: string +} + export type EstimateArgs = { amountIn: number blacklistedDexes?: string[] @@ -17,12 +31,60 @@ export type EstimateArgs = { tokenOut: Portfolio.Token.Id } +export type ReverseEstimateArgs = { + amountOut: number + blacklistedDexes?: string[] + address: string + isOptimized?: boolean + slippage: number + tokenIn: Portfolio.Token.Id + tokenOut: Portfolio.Token.Id +} + +export type LimitOrderArgs = { + amountIn: number + blacklistedDexes?: string[] + address: string + dex?: string + multiples?: number + tokenIn: Portfolio.Token.Id + tokenOut: Portfolio.Token.Id + wantedPrice: number +} + +export type SignArgs = { + signatures: string + txCbor: string +} + +export type SwapArgs = { + amountIn: number + blacklistedDexes?: string[] + address: string + inputs: string[] + slippage: number + tokenIn: Portfolio.Token.Id + tokenOut: Portfolio.Token.Id +} + export type DexhunterApi = { averagePrice: ( args: AveragePriceArgs, ) => Promise>> - orders: (args: OrdersArgs) => Promise>> // untransformed response + orders: (args: OrdersArgs) => Promise>> estimate: ( args: EstimateArgs, - ) => Promise>> // untransformed response + ) => Promise>> + reverseEstimate: ( + args: ReverseEstimateArgs, + ) => Promise>> + swap: (args: SwapArgs) => Promise>> + sign: (args: SignArgs) => Promise>> + cancel: (args: CancelArgs) => Promise>> + limit: ( + args: LimitOrderArgs, + ) => Promise>> + limitEstimate: ( + args: LimitOrderArgs, + ) => Promise>> } diff --git a/packages/swap/src/adapters/dexhunter-api/transformers.ts b/packages/swap/src/adapters/dexhunter-api/transformers.ts index 92d61acf9e..1abc9f8754 100644 --- a/packages/swap/src/adapters/dexhunter-api/transformers.ts +++ b/packages/swap/src/adapters/dexhunter-api/transformers.ts @@ -1,7 +1,31 @@ import {Portfolio} from '@yoroi/types' -import {AveragePriceResponse, EstimateRequest} from './types' +import { + AveragePriceResponse, + CancelRequest, + CancelResponse, + EstimateRequest, + EstimateResponse, + LimitOrderEstimate, + LimitOrderRequest, + LimitOrderResponse, + ReverseEstimateRequest, + ReverseEstimateResponse, + SignRequest, + SignResponse, + SwapRequest, + SwapResponse, +} from './types' import {isPrimaryToken} from '@yoroi/portfolio' -import {AveragePriceArgs, EstimateArgs, OrdersArgs} from './dexhunter' +import { + AveragePriceArgs, + CancelArgs, + EstimateArgs, + LimitOrderArgs, + OrdersArgs, + ReverseEstimateArgs, + SignArgs, + SwapArgs, +} from './dexhunter' const Identity = (v: T) => v @@ -14,12 +38,12 @@ const tokenIdToDexhunter = (tokenId: Portfolio.Token.Id) => isPrimaryToken(tokenId) ? 'ADA' : tokenId.replace('.', '') export const transformers = { - charts: noTransforms, - dcaCancel: noTransforms, - dcaCreate: noTransforms, - dcaEstimate: noTransforms, - dcaByAdress: noTransforms, - markingSubmit: noTransforms, + charts: noTransforms, // unused + dcaCancel: noTransforms, // unused + dcaCreate: noTransforms, // unused + dcaEstimate: noTransforms, // unused + dcaByAdress: noTransforms, // unused + markingSubmit: noTransforms, // unused averagePrice: { request: ({tokenInId, tokenOutId}: AveragePriceArgs) => ({ tokenInId: tokenIdToDexhunter(tokenInId), @@ -27,7 +51,16 @@ export const transformers = { }), response: (data: AveragePriceResponse) => data.averagePrice, }, - cancel: noTransforms, + cancel: { + request: ({address, orderId}: CancelArgs): CancelRequest => ({ + address, + order_id: orderId, + }), + response: ({additional_cancellation_fee, cbor}: CancelResponse) => ({ + cbor, + cancellationFee: additional_cancellation_fee, + }), + }, estimate: { request: ({ amountIn, @@ -36,25 +69,260 @@ export const transformers = { slippage, tokenIn, tokenOut, - }: EstimateArgs) => - ({ - amount_in: amountIn, - blacklisted_dexes: blacklistedDexes, - single_preferred_dex: singlePreferredDex, - slippage, - token_in: tokenIdToDexhunter(tokenIn), - token_out: tokenIdToDexhunter(tokenOut), - } as EstimateRequest), - response: Identity, + }: EstimateArgs): EstimateRequest => ({ + amount_in: amountIn, + blacklisted_dexes: blacklistedDexes, + single_preferred_dex: singlePreferredDex, + slippage, + token_in: tokenIdToDexhunter(tokenIn), + token_out: tokenIdToDexhunter(tokenOut), + }), + response: ({ + average_price, + batcher_fee, + communications, + deposits, + dexhunter_fee, + net_price, + net_price_reverse, + partner_code, + partner_fee, + possible_routes, + splits, + total_fee, + total_output, + total_output_without_slippage, + }: EstimateResponse) => ({ + averagePrice: average_price, + batcherFee: batcher_fee, + communications, + deposits, + dexhunterFee: dexhunter_fee, + netPrice: net_price, + netPriceReverse: net_price_reverse, + partnerCode: partner_code, + partnerFee: partner_fee, + possibleRoutes: possible_routes, + splits, + totalFee: total_fee, + totalOutput: total_output, + totalOutputWithoutSlippage: total_output_without_slippage, + }), + }, + reverseEstimate: { + request: ({ + amountOut, + blacklistedDexes, + address, + isOptimized, + slippage, + tokenIn, + tokenOut, + }: ReverseEstimateArgs): ReverseEstimateRequest => ({ + amount_out: amountOut, + blacklisted_dexes: blacklistedDexes, + buyer_address: address, + is_optimized: isOptimized, + slippage, + token_in: tokenIdToDexhunter(tokenIn), + token_out: tokenIdToDexhunter(tokenOut), + }), + response: ({ + average_price, + batcher_fee, + communications, + deposits, + dexhunter_fee, + net_price, + net_price_reverse, + partner_fee, + possible_routes, + price_ab, + price_ba, + splits, + total_fee, + total_input, + total_input_without_slippage, + total_output, + }: ReverseEstimateResponse) => ({ + averagePrice: average_price, + batcherFee: batcher_fee, + communications, + deposits, + dexhunterFee: dexhunter_fee, + netPrice: net_price, + netPriceReverse: net_price_reverse, + partnerFee: partner_fee, + possibleRoutes: possible_routes, + priceAB: price_ab, + priceBA: price_ba, + splits, + totalFee: total_fee, + totalInput: total_input, + totalInputWithoutSlippage: total_input_without_slippage, + totalOutput: total_output, + }), + }, + limit: { + request: ({ + amountIn, + blacklistedDexes, + address, + dex, + multiples, + tokenIn, + tokenOut, + wantedPrice, + }: LimitOrderArgs): LimitOrderRequest => ({ + amount_in: amountIn, + blacklisted_dexes: blacklistedDexes, + buyer_address: address, + dex: dex, + multiples, + token_in: tokenIdToDexhunter(tokenIn), + token_out: tokenIdToDexhunter(tokenOut), + wanted_price: wantedPrice, + }), + response: ({ + batcher_fee, + cbor, + deposits, + dexhunter_fee, + partner, + partner_fee, + possible_routes, + splits, + totalFee, + total_input, + total_output, + }: LimitOrderResponse) => ({ + batcherFee: batcher_fee, + cbor, + deposits, + dexhunterFee: dexhunter_fee, + partner, + partnerFee: partner_fee, + possibleRoutes: possible_routes, + splits, + totalFee, + totalInput: total_input, + totalOutput: total_output, + }), + }, + limitEstimate: { + request: ({ + amountIn, + blacklistedDexes, + address, + dex, + multiples, + tokenIn, + tokenOut, + wantedPrice, + }: LimitOrderArgs): LimitOrderRequest => ({ + amount_in: amountIn, + blacklisted_dexes: blacklistedDexes, + buyer_address: address, + dex: dex, + multiples, + token_in: tokenIdToDexhunter(tokenIn), + token_out: tokenIdToDexhunter(tokenOut), + wanted_price: wantedPrice, + }), + response: ({ + batcher_fee, + blacklisted_dexes, + deposits, + dexhunter_fee, + net_price, + partner, + partner_fee, + possible_routes, + splits, + total_fee, + total_input, + total_output, + }: LimitOrderEstimate) => ({ + batcherFee: batcher_fee, + blacklistedDexes: blacklisted_dexes, + deposits, + dexhunterFee: dexhunter_fee, + netPrice: net_price, + partner, + partnerFee: partner_fee, + possibleRoutes: possible_routes, + splits, + totalFee: total_fee, + totalInput: total_input, + totalOutput: total_output, + }), }, - limit: noTransforms, - limitEstimate: noTransforms, orders: { request: ({address}: OrdersArgs) => ({userAddress: address}), response: Identity, }, - reverseEstimate: noTransforms, - sign: noTransforms, - swap: noTransforms, - wallet: noTransforms, + sign: { + request: ({signatures, txCbor}: SignArgs): SignRequest => ({ + Signatures: signatures, + txCbor, + }), + response: ({cbor, strat_id}: SignResponse) => ({cbor, stratId: strat_id}), + }, + swap: { + request: ({ + amountIn, + blacklistedDexes, + address, + inputs, + slippage, + tokenIn, + tokenOut, + }: SwapArgs): SwapRequest => ({ + amount_in: amountIn, + blacklisted_dexes: blacklistedDexes, + buyer_address: address, + inputs, + slippage, + token_in: tokenIdToDexhunter(tokenIn), + token_out: tokenIdToDexhunter(tokenOut), + }), + response: ({ + average_price, + batcher_fee, + cbor, + communications, + deposits, + dexhunter_fee, + net_price, + net_price_reverse, + partner_code, + partner_fee, + possible_routes, + splits, + total_fee, + total_input, + total_input_without_slippage, + total_output, + total_output_without_slippage, + }: SwapResponse) => ({ + averagePrice: average_price, + batcherFee: batcher_fee, + cbor, + communications, + deposits, + dexhunterFee: dexhunter_fee, + netPrice: net_price, + netPriceReverse: net_price_reverse, + partnerCode: partner_code, + partnerFee: partner_fee, + possibleRoutes: possible_routes, + splits, + totalFee: total_fee, + totalInput: total_input, + totalInputWithoutSlippage: total_input_without_slippage, + totalOutput: total_output, + totalOutputWithoutSlippage: total_output_without_slippage, + }), + }, + wallet: noTransforms, // unused } as const