UNPKG

@yoroi/swap

Version:
376 lines (350 loc) 11.2 kB
import {Portfolio, Swap} from '@yoroi/types' import { CancelRequest, CancelResponse, CreateOrderRequest, CreateOrderResponse, OrdersHistoryResponse, LimitOrderRequest, LimitOrderResponse, LimitQuoteRequest, Dex, QuoteRequest, QuoteResponse, Split, TokensResponse, MuesliswapApiConfig, } from './types' export const transformersMaker = ({ primaryTokenInfo, address, partner, }: MuesliswapApiConfig) => { return { tokens: { response: (res: TokensResponse): Array<Portfolio.Token.Info> => res .map(({ticker, name, policyId, hexName, decimals, verified}) => { const id = `${policyId}.${hexName}` const isPrimary = id === primaryTokenInfo.id if (isPrimary) return primaryTokenInfo if (decimals === null) return null return { status: verified ? Portfolio.Token.Status.Valid : Portfolio.Token.Status.Invalid, id, ticker, name, type: Portfolio.Token.Type.FT, nature: Portfolio.Token.Nature.Secondary, application: Portfolio.Token.Application.General, fingerprint: '', decimals, description: '', originalImage: '', symbol: '', reference: '', tag: '', website: '', } }) .filter((v): v is Portfolio.Token.Info => !!v), }, ordersHistory: { response: ({orders}: OrdersHistoryResponse): Array<Swap.Order> => orders.map( ({ dex, outputIdx, fromToken, toToken, placedAt, finalizedAt, receivedAmount, toAmount, fromAmount, txHash, finalizedTxHash, status, }) => ({ status, txHash, aggregator: Swap.Aggregator.Muesliswap, outputIndex: outputIdx, tokenIn: fromToken, tokenOut: toToken, updateTxHash: finalizedTxHash ?? txHash, placedAt: placedAt ? placedAt * 1000 : undefined, lastUpdate: finalizedAt ? finalizedAt * 1000 : undefined, amountIn: Number(fromAmount), actualAmountOut: Number(receivedAmount), expectedAmountOut: Number(toAmount), protocol: toSwapProtocol(dex), }), ), }, cancel: { request: ({order}: Swap.CancelRequest): CancelRequest => ({ order_ids: [`${order.txHash}#${order.outputIndex ?? 0}`], }), response: ({tx_cbor}: CancelResponse): Swap.CancelResponse => ({ cbor: tx_cbor, }), }, limitQuote: { request: ({ amountIn = 0, wantedPrice = 0, protocol, tokenIn, tokenOut, }: Swap.EstimateRequest): LimitQuoteRequest => ({ numbers_have_decimals: true, sell_token: tokenIn, buy_token: tokenOut, sell_amount: String(amountIn), buy_amount: String(amountIn * wantedPrice), order_contract: protocol ? fromSwapProtocol(protocol) : undefined, }), }, quote: { request: ({ protocol, blockedProtocols, tokenIn, tokenOut, amountIn, amountOut, slippage, }: Swap.EstimateRequest): QuoteRequest => ({ numbers_have_decimals: true, sell_token: tokenIn, buy_token: tokenOut, ...(amountOut !== undefined && {buy_amount: String(amountOut)}), ...(amountIn !== undefined && {sell_amount: String(amountIn)}), ...(partner !== undefined && {partner}), // muesli expects slippage as a percentage slippage: slippage / 100, excluded_sources: protocol ? Object.values(Dex).filter( (dex) => dex !== Dex.Unsupported && dex !== fromSwapProtocol(protocol), ) : (blockedProtocols?.map(fromSwapProtocol) ?? []), }), response: ({ buy_token_decimals, sell_token_decimals, net_price, net_price_impact, splits, total_batcher_fee, total_deposit, total_input, service_fee, total_output, total_output_without_slippage, }: QuoteResponse): Swap.EstimateResponse => ({ aggregatorFee: 0, frontendFee: 0, netPrice: net_price * 10 ** (sell_token_decimals - buy_token_decimals), priceImpact: net_price_impact, batcherFee: Number(total_batcher_fee), deposits: Number(total_deposit), totalFee: Number( (Number(total_batcher_fee) + Number(service_fee)).toFixed( primaryTokenInfo.decimals, ), ), totalInput: Number(total_input), totalOutput: Number(total_output), totalOutputWithoutSlippage: total_output_without_slippage === undefined ? undefined : Number(total_output_without_slippage), splits: splits.map(toSwapSplit), }), }, create: { request: ({ slippage = 0, protocol, blockedProtocols, tokenIn, tokenOut, amountIn, inputs, }: Swap.CreateRequest): CreateOrderRequest => ({ numbers_have_decimals: true, sell_token: tokenIn, buy_token: tokenOut, sell_amount: String(amountIn), user_address: address, ...(partner !== undefined && {partner}), slippage: slippage / 100, excluded_sources: protocol ? Object.values(Dex).filter( (dex) => dex !== Dex.Unsupported && dex !== fromSwapProtocol(protocol), ) : (blockedProtocols?.map(fromSwapProtocol) ?? []), utxos: inputs, }), response: ({ quote: { buy_token_decimals, sell_token_decimals, net_price, net_price_impact, splits, service_fee, total_batcher_fee, total_deposit, total_input, total_output, total_output_without_slippage, }, tx_cbor, }: CreateOrderResponse): Swap.CreateResponse => ({ aggregator: Swap.Aggregator.Muesliswap, aggregatorFee: 0, frontendFee: Number(service_fee), cbor: tx_cbor, netPrice: net_price * 10 ** (sell_token_decimals - buy_token_decimals), priceImpact: net_price_impact, batcherFee: Number(total_batcher_fee), deposits: Number(total_deposit), totalFee: Number( (Number(total_batcher_fee) + Number(service_fee)).toFixed( primaryTokenInfo.decimals, ), ), totalInput: Number(total_input), totalOutput: Number(total_output), totalOutputWithoutSlippage: Number(total_output_without_slippage), splits: splits.map(toSwapSplit), }), }, createLimit: { request: ({ protocol = Swap.Protocol.Unsupported, wantedPrice = 0, tokenIn, tokenOut, amountIn, inputs, }: Swap.CreateRequest): LimitOrderRequest => ({ order_contract: fromSwapProtocol(protocol), buy_amount: String(amountIn * wantedPrice), numbers_have_decimals: true, sell_token: tokenIn, buy_token: tokenOut, sell_amount: String(amountIn), user_address: address, utxos: inputs, }), response: ({ quote: { buy_token_decimals, sell_token_decimals, net_price, net_price_impact, splits, service_fee, total_batcher_fee, total_deposit, total_input, total_output, total_output_without_slippage, }, tx_cbor, }: LimitOrderResponse): Swap.CreateResponse => ({ cbor: tx_cbor, aggregator: Swap.Aggregator.Muesliswap, aggregatorFee: 0, frontendFee: Number(service_fee), netPrice: net_price * 10 ** (sell_token_decimals - buy_token_decimals), priceImpact: net_price_impact, batcherFee: Number(total_batcher_fee), deposits: Number(total_deposit), totalFee: Number( (Number(total_batcher_fee) + Number(service_fee)).toFixed( primaryTokenInfo.decimals, ), ), totalInput: Number(total_input), totalOutput: Number(total_output), totalOutputWithoutSlippage: Number(total_output_without_slippage), splits: splits.map(toSwapSplit), }), }, } as const } const toSwapSplit = ({ amount_in, batcher_fee, deposit, dex, expected_output, expected_output_without_slippage, final_price, initial_price, pool_fee, price_impact, price_distortion, source_id, }: Split): Swap.Split => ({ fee: pool_fee, finalPrice: final_price, initialPrice: initial_price, poolFee: pool_fee, poolId: source_id, priceDistortion: price_distortion, priceImpact: price_impact, amountIn: Number(amount_in), batcherFee: Number(batcher_fee), deposits: Number(deposit), expectedOutput: Number(expected_output), expectedOutputWithoutSlippage: Number( expected_output_without_slippage ?? expected_output, ), protocol: toSwapProtocol(dex), }) export const toSwapProtocol = (dex: Dex): Swap.Protocol => ({ [Dex.Minswap_v1]: Swap.Protocol.Minswap_v1, [Dex.Minswap_v2]: Swap.Protocol.Minswap_v2, [Dex.Minswap_stable]: Swap.Protocol.Minswap_stable, [Dex.Wingriders_v1]: Swap.Protocol.Wingriders_v1, [Dex.Wingriders_v2]: Swap.Protocol.Wingriders_v2, [Dex.Vyfi_v1]: Swap.Protocol.Vyfi_v1, [Dex.Sundaeswap_v1]: Swap.Protocol.Sundaeswap_v1, [Dex.Sundaeswap_v3]: Swap.Protocol.Sundaeswap_v3, [Dex.Muesliswap_v2]: Swap.Protocol.Muesliswap_v2, [Dex.Muesliswap_clp]: Swap.Protocol.Muesliswap_clp, [Dex.Spectrum_v1]: Swap.Protocol.Spectrum_v1, [Dex.Teddy_v1]: Swap.Protocol.Teddy_v1, [Dex.Unsupported]: Swap.Protocol.Unsupported, })[dex] ?? Swap.Protocol.Unsupported export const fromSwapProtocol = (dex: Swap.Protocol): Dex => ({ [Swap.Protocol.Cswap]: Dex.Unsupported, [Swap.Protocol.Minswap_v1]: Dex.Minswap_v1, [Swap.Protocol.Minswap_v2]: Dex.Minswap_v2, [Swap.Protocol.Minswap_stable]: Dex.Minswap_stable, [Swap.Protocol.Wingriders_v1]: Dex.Wingriders_v1, [Swap.Protocol.Wingriders_v2]: Dex.Wingriders_v2, [Swap.Protocol.Vyfi_v1]: Dex.Vyfi_v1, [Swap.Protocol.Sundaeswap_v1]: Dex.Sundaeswap_v1, [Swap.Protocol.Sundaeswap_v3]: Dex.Sundaeswap_v3, [Swap.Protocol.Splash_v1]: Dex.Unsupported, [Swap.Protocol.Teddy_v1]: Dex.Teddy_v1, [Swap.Protocol.Muesliswap_v2]: Dex.Muesliswap_v2, [Swap.Protocol.Muesliswap_clp]: Dex.Muesliswap_clp, [Swap.Protocol.Spectrum_v1]: Dex.Spectrum_v1, [Swap.Protocol.Unsupported]: Dex.Unsupported, })[dex] ?? Dex.Unsupported export const MuesliswapProtocols = Object.values(Dex) .filter((p) => p !== Dex.Unsupported) .map(toSwapProtocol)