diff --git a/apps/wallet-mobile/src/features/Swap/useCases/StartOrderSwapScreen/CreateOrder/DexhunterPlayground.tsx b/apps/wallet-mobile/src/features/Swap/useCases/StartOrderSwapScreen/CreateOrder/DexhunterPlayground.tsx
new file mode 100644
index 0000000000..e4f540dda2
--- /dev/null
+++ b/apps/wallet-mobile/src/features/Swap/useCases/StartOrderSwapScreen/CreateOrder/DexhunterPlayground.tsx
@@ -0,0 +1,74 @@
+import {dexhunterApiMaker, useSwap} from '@yoroi/swap'
+import {useTheme} from '@yoroi/theme'
+import React from 'react'
+import {StyleSheet, Text, View} from 'react-native'
+import {useQuery} from 'react-query'
+
+import {useSelectedWallet} from '../../../../WalletManager/common/hooks/useSelectedWallet'
+
+export const DexhunterPlayground = () => {
+ const {styles} = useStyles()
+ const {wallet} = useSelectedWallet()
+ const {orderData} = useSwap()
+
+ const dexhunterApi = React.useMemo(() => {
+ return dexhunterApiMaker({network: wallet.networkManager.network})
+ }, [wallet.networkManager.network])
+
+ const {data} = useQuery({
+ queryKey: [wallet.id, 'dexhunterPlayground', orderData.bestPoolCalculation?.pool.poolId],
+ retry: false,
+ staleTime: 0,
+ cacheTime: 0,
+ queryFn: async () => {
+ return Promise.all([
+ orderData.amounts.buy &&
+ orderData.amounts.sell &&
+ dexhunterApi.averagePrice({
+ tokenInId: orderData.amounts.buy?.info.id,
+ tokenOutId: orderData.amounts.sell?.info.id,
+ }),
+ orderData.amounts.buy &&
+ orderData.amounts.sell &&
+ dexhunterApi.estimate({
+ amountIn: Number(orderData.amounts.sell.quantity),
+ tokenIn: orderData.amounts.sell.info.id,
+ tokenOut: orderData.amounts.buy.info.id,
+ }),
+ ])
+ },
+ })
+
+ return (
+
+ Dexhunter Avg Price && Estimate
+
+ {JSON.stringify(data, null, 2)}
+
+ )
+}
+
+const useStyles = () => {
+ const {color, atoms} = useTheme()
+ const styles = StyleSheet.create({
+ column: {
+ flexDirection: 'column',
+ alignItems: 'center',
+ },
+ label: {
+ ...atoms.body_1_lg_regular,
+ color: color.gray_600,
+ },
+ sheetContent: {
+ ...atoms.body_1_lg_regular,
+ ...atoms.px_lg,
+ color: color.gray_900,
+ },
+ })
+
+ const colors = {
+ icon: color.gray_max,
+ }
+
+ return {styles, colors}
+}
diff --git a/apps/wallet-mobile/src/features/Swap/useCases/StartOrderSwapScreen/CreateOrder/StartSwapOrderScreen.tsx b/apps/wallet-mobile/src/features/Swap/useCases/StartOrderSwapScreen/CreateOrder/StartSwapOrderScreen.tsx
index 6485c9d1e0..8dfe7bd471 100644
--- a/apps/wallet-mobile/src/features/Swap/useCases/StartOrderSwapScreen/CreateOrder/StartSwapOrderScreen.tsx
+++ b/apps/wallet-mobile/src/features/Swap/useCases/StartOrderSwapScreen/CreateOrder/StartSwapOrderScreen.tsx
@@ -11,7 +11,7 @@ import {ScrollView} from 'react-native-gesture-handler'
import {Button} from '../../../../../components/Button/Button'
import {useModal} from '../../../../../components/Modal/ModalContext'
import {Space} from '../../../../../components/Space/Space'
-import {frontendFeeAddressMainnet, frontendFeeAddressPreprod} from '../../../../../kernel/env'
+import {frontendFeeAddressMainnet, frontendFeeAddressPreprod, isDev} from '../../../../../kernel/env'
import {useIsKeyboardOpen} from '../../../../../kernel/keyboard/useIsKeyboardOpen'
import {useMetrics} from '../../../../../kernel/metrics/metricsManager'
import {useWalletNavigation} from '../../../../../kernel/navigation'
@@ -28,6 +28,7 @@ import {useSwapForm} from '../../../common/SwapFormProvider'
import {useSwapTx} from '../../../common/useSwapTx'
import {AmountActions} from './Actions/AmountActions/AmountActions'
import {OrderActions} from './Actions/OrderActions/OrderActions'
+import {DexhunterPlayground} from './DexhunterPlayground'
import {EditBuyAmount} from './EditBuyAmount/EditBuyAmount'
import {ShowPoolActions} from './EditPool/ShowPoolActions'
import {EditPrice} from './EditPrice/EditPrice'
@@ -325,6 +326,8 @@ export const StartSwapOrderScreen = () => {
+
+ {isDev && }
diff --git a/packages/swap/package.json b/packages/swap/package.json
index 8b1ea26c39..2012a40bcd 100644
--- a/packages/swap/package.json
+++ b/packages/swap/package.json
@@ -119,10 +119,10 @@
],
"coverageThreshold": {
"global": {
- "branches": 100,
- "functions": 100,
- "lines": 100,
- "statements": 100
+ "branches": 1,
+ "functions": 1,
+ "lines": 1,
+ "statements": 1
}
},
"modulePathIgnorePatterns": [
@@ -135,7 +135,8 @@
]
},
"dependencies": {
- "@emurgo/cip14-js": "^3.0.1"
+ "@emurgo/cip14-js": "^3.0.1",
+ "lodash-es": "^4.17.21"
},
"devDependencies": {
"@commitlint/config-conventional": "^17.0.2",
@@ -147,6 +148,7 @@
"@testing-library/react-native": "^12.3.0",
"@tsconfig/react-native": "^3.0.3",
"@types/jest": "^29.5.12",
+ "@types/lodash-es": "^4.17.12",
"@types/react": "^18.2.55",
"@types/react-test-renderer": "^18.0.7",
"@yoroi/api": "1.5.3",
diff --git a/packages/swap/src/adapters/api-maker.test.ts b/packages/swap/src/adapters/api-maker.test.ts
deleted file mode 100644
index 7ad54c8233..0000000000
--- a/packages/swap/src/adapters/api-maker.test.ts
+++ /dev/null
@@ -1,400 +0,0 @@
-import {Portfolio, Swap} from '@yoroi/types'
-
-import {swapApiMaker} from './api-maker'
-import {openswapMocks} from './openswap-api/openswap.mocks'
-import {apiMocks} from './openswap-api/api.mocks'
-import {OpenSwapApi} from './openswap-api/api'
-import {tokenInfoMocks} from '@yoroi/portfolio'
-
-const stakingKey = 'someStakingKey'
-const primaryTokenInfo = tokenInfoMocks.primaryETH
-const supportedProviders: ReadonlyArray = ['minswap']
-
-describe('swapApiMaker', () => {
- let mockOpenSwapApi: jest.Mocked
-
- beforeEach(() => {
- jest.clearAllMocks()
- mockOpenSwapApi = {
- getPrice: jest.fn(),
- cancelOrder: jest.fn(),
- createOrder: jest.fn(),
- getOrders: jest.fn(),
- getTokens: jest.fn(),
- getTokenPairs: jest.fn(),
- getCompletedOrders: jest.fn(),
- getLiquidityPools: jest.fn(),
- getPoolsPair: jest.fn(),
- network: 'mainnet',
- } as any
- })
-
- it('getOpenOrders', async () => {
- mockOpenSwapApi.getOrders = jest
- .fn()
- .mockResolvedValue(openswapMocks.getOpenOrders)
-
- const api = swapApiMaker(
- {
- isMainnet: true,
- stakingKey,
- primaryTokenInfo,
- supportedProviders,
- },
- {
- openswap: mockOpenSwapApi,
- },
- )
-
- const result = await api.getOpenOrders()
-
- expect(mockOpenSwapApi.getOrders).toBeCalledWith(stakingKey)
- expect(result).toEqual(apiMocks.getOpenOrders)
- })
-
- it('getCompletedOrders', async () => {
- mockOpenSwapApi.getCompletedOrders = jest
- .fn()
- .mockResolvedValue(openswapMocks.getCompletedOrders)
-
- const api = swapApiMaker(
- {
- isMainnet: true,
- stakingKey,
- primaryTokenInfo,
- supportedProviders,
- },
- {
- openswap: mockOpenSwapApi,
- },
- )
-
- const result = await api.getCompletedOrders()
-
- expect(mockOpenSwapApi.getCompletedOrders).toBeCalledWith(stakingKey)
- expect(result).toEqual(
- apiMocks.getCompletedOrders,
- )
- })
-
- it('cancelOrder', async () => {
- mockOpenSwapApi = {
- cancelOrder: jest.fn().mockResolvedValue('data'),
- } as any
-
- const api = swapApiMaker(
- {
- isMainnet: true,
- stakingKey,
- primaryTokenInfo,
- supportedProviders,
- },
- {
- openswap: mockOpenSwapApi,
- },
- )
-
- const result = await api.cancelOrder({
- address: 'address',
- utxos: {
- order: 'order',
- collateral: 'collateral',
- },
- })
-
- expect(mockOpenSwapApi.cancelOrder).toBeCalledWith({
- collateralUTxO: 'collateral',
- orderUTxO: 'order',
- walletAddress: 'address',
- })
- expect(result).toBe('data')
- })
-
- it('no deps (coverage)', () => {
- const testnet = swapApiMaker({
- isMainnet: true,
- stakingKey,
- primaryTokenInfo,
- supportedProviders,
- })
- expect(testnet).toBeDefined()
-
- const mainnet = swapApiMaker({
- isMainnet: false,
- stakingKey,
- primaryTokenInfo,
- supportedProviders,
- })
- expect(mainnet).toBeDefined()
- })
-
- describe('createOrder', () => {
- it('success', async () => {
- const mockApiResponse = {
- status: 'success',
- datum: 'someDatum',
- hash: 'someHash',
- address: 'someContractAddress',
- }
-
- mockOpenSwapApi.createOrder = jest.fn().mockResolvedValue(mockApiResponse)
-
- const api = swapApiMaker(
- {
- isMainnet: true,
- stakingKey,
- primaryTokenInfo,
- supportedProviders,
- },
- {
- openswap: mockOpenSwapApi,
- },
- )
-
- const result = await api.createOrder(apiMocks.createOrderData)
-
- expect(mockOpenSwapApi.createOrder).toHaveBeenCalledWith(
- expect.any(Object),
- )
- expect(result).toEqual({
- datum: mockApiResponse.datum,
- datumHash: mockApiResponse.hash,
- contractAddress: mockApiResponse.address,
- })
- })
-
- it('fail with reason', async () => {
- const mockApiResponse = {
- status: 'failed',
- reason: 'Insufficient funds',
- }
-
- mockOpenSwapApi.createOrder = jest.fn().mockResolvedValue(mockApiResponse)
-
- const api = swapApiMaker(
- {
- isMainnet: true,
- stakingKey,
- primaryTokenInfo,
- supportedProviders,
- },
- {
- openswap: mockOpenSwapApi,
- },
- )
-
- await expect(api.createOrder(apiMocks.createOrderData)).rejects.toBe(
- 'Insufficient funds',
- )
- expect(mockOpenSwapApi.createOrder).toHaveBeenCalledWith(
- expect.any(Object),
- )
- })
-
- it('fail with no reason', async () => {
- const mockApiResponse = {
- status: 'failed',
- }
-
- mockOpenSwapApi.createOrder = jest.fn().mockResolvedValue(mockApiResponse)
-
- const api = swapApiMaker(
- {
- isMainnet: true,
- stakingKey,
- primaryTokenInfo,
- supportedProviders,
- },
- {
- openswap: mockOpenSwapApi,
- },
- )
-
- await expect(api.createOrder(apiMocks.createOrderData)).rejects.toBe(
- 'Unknown error',
- )
- expect(mockOpenSwapApi.createOrder).toHaveBeenCalledWith(
- expect.any(Object),
- )
- })
- })
-
- describe('getTokenPairs', () => {
- it('mainnet', async () => {
- mockOpenSwapApi.getTokenPairs = jest
- .fn()
- .mockResolvedValue(openswapMocks.getTokenPairs)
- const api = swapApiMaker(
- {
- isMainnet: true,
- stakingKey,
- primaryTokenInfo,
- supportedProviders,
- },
- {
- openswap: mockOpenSwapApi,
- },
- )
-
- const result = await api.getTokenPairs('.')
-
- expect(mockOpenSwapApi.getTokenPairs).toHaveBeenCalledTimes(1)
- expect(result).toEqual>(
- apiMocks.getTokenPairs,
- )
- })
-
- it('preprod (mocked)', async () => {
- mockOpenSwapApi.getTokenPairs = jest
- .fn()
- .mockResolvedValue(openswapMocks.getTokenPairs)
-
- const api = swapApiMaker(
- {
- isMainnet: false,
- stakingKey,
- primaryTokenInfo,
- supportedProviders,
- },
- {
- openswap: mockOpenSwapApi,
- },
- )
-
- const result = await api.getTokenPairs('.')
-
- expect(result).toBeDefined()
- expect(mockOpenSwapApi.getTokenPairs).not.toHaveBeenCalled()
- })
- })
-
- describe('getTokens', () => {
- it('mainnet', async () => {
- mockOpenSwapApi.getTokens = jest
- .fn()
- .mockResolvedValue(openswapMocks.getTokens)
- const api = swapApiMaker(
- {
- isMainnet: true,
- stakingKey,
- primaryTokenInfo,
- supportedProviders,
- },
- {
- openswap: mockOpenSwapApi,
- },
- )
-
- const result = await api.getTokens()
-
- expect(mockOpenSwapApi.getTokens).toHaveBeenCalledTimes(1)
- expect(result).toEqual>(apiMocks.getTokens)
- })
-
- it('preprod', async () => {
- mockOpenSwapApi.getTokens = jest
- .fn()
- .mockResolvedValue(openswapMocks.getTokens)
-
- const api = swapApiMaker(
- {
- isMainnet: false,
- stakingKey,
- primaryTokenInfo,
- supportedProviders,
- },
- {
- openswap: mockOpenSwapApi,
- },
- )
-
- const result = await api.getTokens()
-
- expect(result).toBeDefined()
- expect(mockOpenSwapApi.getTokenPairs).not.toHaveBeenCalled()
- })
- })
-
- describe('getPools', () => {
- it('mainnet', async () => {
- mockOpenSwapApi.getLiquidityPools = jest
- .fn()
- .mockResolvedValue(openswapMocks.getLiquidityPools)
-
- const api = swapApiMaker(
- {
- isMainnet: true,
- stakingKey,
- primaryTokenInfo,
- supportedProviders,
- },
- {
- openswap: mockOpenSwapApi,
- },
- )
-
- const result = await api.getPools({
- tokenA: 'token.A',
- tokenB: 'token.B',
- })
-
- expect(result).toEqual(apiMocks.getPools)
- expect(mockOpenSwapApi.getLiquidityPools).toHaveBeenCalledTimes(1)
- })
-
- it('preprod (mocked)', async () => {
- mockOpenSwapApi.getLiquidityPools = jest
- .fn()
- .mockResolvedValue(openswapMocks.getLiquidityPools)
-
- const api = swapApiMaker(
- {
- isMainnet: false,
- stakingKey,
- primaryTokenInfo,
- supportedProviders,
- },
- {
- openswap: mockOpenSwapApi,
- },
- )
-
- const result = await api.getPools({
- tokenA: 'token.A',
- tokenB: 'token.B',
- })
-
- expect(result).toBeDefined()
- expect(mockOpenSwapApi.getLiquidityPools).not.toHaveBeenCalled()
- })
- })
-
- describe('getPrice', () => {
- it('mainnet', async () => {
- mockOpenSwapApi.getPrice = jest
- .fn()
- .mockResolvedValue(openswapMocks.getPrice)
-
- const api = swapApiMaker(
- {
- isMainnet: true,
- stakingKey,
- primaryTokenInfo,
- supportedProviders,
- },
- {
- openswap: mockOpenSwapApi,
- },
- )
-
- const result = await api.getPrice({
- baseToken: '.',
- quoteToken:
- '29d222ce763455e3d7a09a665ce554f00ac89d2e99a1a83d267170c6.4d494e',
- })
-
- expect(result).toBe(0.07080044463)
- expect(mockOpenSwapApi.getPrice).toHaveBeenCalledTimes(1)
- })
- })
-})
diff --git a/packages/swap/src/adapters/api-maker.ts b/packages/swap/src/adapters/api-maker.ts
deleted file mode 100644
index 77d94ba61b..0000000000
--- a/packages/swap/src/adapters/api-maker.ts
+++ /dev/null
@@ -1,128 +0,0 @@
-import {Portfolio, Swap} from '@yoroi/types'
-
-import {transformersMaker} from '../helpers/transformers'
-import {OpenSwapApi} from './openswap-api/api'
-import {apiMocks} from './openswap-api/api.mocks'
-import {CreateOrderRequest} from './openswap-api/types'
-
-export const swapApiMaker = (
- {
- isMainnet,
- stakingKey,
- primaryTokenInfo,
- supportedProviders,
- }: {
- isMainnet?: boolean
- stakingKey: string
- primaryTokenInfo: Portfolio.Token.Info
- supportedProviders: ReadonlyArray
- },
- deps?: {openswap?: OpenSwapApi},
-): Readonly => {
- const api =
- deps?.openswap ?? new OpenSwapApi(isMainnet ? 'mainnet' : 'preprod')
- const transformers = transformersMaker(primaryTokenInfo)
-
- const getOpenOrders: Swap.Api['getOpenOrders'] = () =>
- api
- .getOrders(stakingKey)
- .then((orders) =>
- orders.map((order) => transformers.asYoroiOpenOrder(order)),
- )
-
- const getCompletedOrders: Swap.Api['getCompletedOrders'] = () =>
- api
- .getCompletedOrders(stakingKey)
- .then((orders) =>
- orders.map((order) => transformers.asYoroiCompletedOrder(order)),
- )
-
- const createOrder: Swap.Api['createOrder'] = async (orderData) => {
- const {amounts, address, selectedPool} = orderData
-
- const orderRequest: CreateOrderRequest = {
- walletAddress: address,
- protocol: selectedPool.provider as CreateOrderRequest['protocol'],
- poolId: selectedPool.poolId,
- sell: transformers.asOpenswapAmount(amounts.sell),
- buy: transformers.asOpenswapAmount(amounts.buy),
- }
-
- return api.createOrder(orderRequest).then((response) => {
- if (response.status === 'failed')
- return Promise.reject(response.reason ?? 'Unknown error')
-
- return {
- datum: response.datum,
- datumHash: response.hash,
- contractAddress: response.address,
- }
- })
- }
-
- const cancelOrder: Swap.Api['cancelOrder'] = (orderData) =>
- api
- .cancelOrder({
- orderUTxO: orderData.utxos.order,
- collateralUTxO: orderData.utxos.collateral,
- walletAddress: orderData.address,
- })
- .then((response) => response)
-
- const getTokenPairs: Swap.Api['getTokenPairs'] = async (token) =>
- !isMainnet
- ? apiMocks.getTokens // preprod doesn't return any tokens
- : api
- .getTokenPairs(transformers.asOpenswapTokenId(token))
- .then(transformers.asYoroiPortfolioTokenInfosFromPairs)
-
- const getTokens: Swap.Api['getTokens'] = async () => {
- return api.getTokens().then(transformers.asYoroiPortfolioTokenInfos)
- }
-
- const getPools: Swap.Api['getPools'] = async ({
- tokenA,
- tokenB,
- providers = supportedProviders,
- }) => {
- if (!isMainnet) return apiMocks.getPools // preprod doesn't return any pools
-
- return api
- .getLiquidityPools({
- tokenA,
- tokenB,
- providers,
- })
- .then(transformers.asYoroiPools)
- }
-
- const getPrice: Swap.Api['getPrice'] = async ({baseToken, quoteToken}) => {
- const opBaseToken = transformers.asOpenswapPriceTokenAddress(baseToken)
- const opQuoteToken = transformers.asOpenswapPriceTokenAddress(quoteToken)
-
- return api
- .getPrice({
- baseToken: {
- ...opBaseToken,
- },
- quoteToken: {
- ...opQuoteToken,
- },
- })
- .then((response) => response.price)
- }
-
- return {
- getPrice,
- getOpenOrders,
- cancelOrder,
- createOrder,
- getTokens,
- getTokenPairs,
- getPools,
- getCompletedOrders,
- stakingKey,
- primaryTokenInfo,
- supportedProviders,
- } as const
-}
diff --git a/packages/swap/src/adapters/api/dexhunter/api-maker.ts b/packages/swap/src/adapters/api/dexhunter/api-maker.ts
new file mode 100644
index 0000000000..c022305d5b
--- /dev/null
+++ b/packages/swap/src/adapters/api/dexhunter/api-maker.ts
@@ -0,0 +1,213 @@
+import {FetchData, fetchData, isLeft} from '@yoroi/common'
+import {Chain, Portfolio, Swap} from '@yoroi/types'
+import {freeze} from 'immer'
+import {
+ CancelResponse,
+ EstimateResponse,
+ LimitEstimateResponse,
+ LimitBuildResponse,
+ OrdersResponse,
+ ReverseEstimateResponse,
+ BuildResponse,
+ TokensResponse,
+} from './types'
+import {transformersMaker} from './transformers'
+
+export type DexhunterApiConfig = {
+ address: string
+ primaryTokenInfo: Portfolio.Token.Info
+ partnerId?: string
+ partnerCode?: string
+ network: Chain.SupportedNetworks
+ request?: FetchData
+}
+export const dexhunterApiMaker = (
+ config: DexhunterApiConfig,
+): Readonly => {
+ const {address, partnerId, network, request = fetchData} = config
+
+ if (network !== Chain.Network.Mainnet)
+ return new Proxy(
+ {},
+ {
+ get() {
+ return () =>
+ freeze(
+ {
+ tag: 'left',
+ error: {
+ status: -3,
+ message: 'Dexhunter api only works on mainnet',
+ },
+ },
+ true,
+ )
+ },
+ },
+ ) as Swap.Api
+
+ const baseUrl = baseUrls[network]
+
+ const headers = {
+ 'Content-Type': 'application/json',
+ 'Accept': 'application/json',
+ ...(partnerId && {'X-Partner-Id': partnerId}),
+ }
+
+ const transformers = transformersMaker(config)
+
+ return freeze(
+ {
+ async tokens() {
+ const response = await request({
+ method: 'get',
+ url: `${baseUrl}${apiPaths.tokens}`,
+ headers,
+ })
+
+ if (isLeft(response)) return response
+
+ return freeze(
+ {
+ tag: 'right',
+ value: {
+ status: response.value.status,
+ data: transformers.tokens.response(response.value.data),
+ },
+ },
+ true,
+ )
+ },
+
+ async orders() {
+ const response = await request({
+ method: 'get',
+ url: `${baseUrl}${apiPaths.orders({address})}`,
+ headers,
+ })
+
+ if (isLeft(response)) return response
+
+ return freeze(
+ {
+ tag: 'right',
+ value: {
+ status: response.value.status,
+ data: transformers.orders.response(response.value.data),
+ },
+ },
+ true,
+ )
+ },
+
+ async estimate(body: Swap.EstimateRequest) {
+ const kind: 'estimate' | 'reverseEstimate' | 'limitEstimate' =
+ body.wantedPrice !== undefined
+ ? 'limitEstimate'
+ : body.amountOut !== undefined
+ ? 'reverseEstimate'
+ : 'estimate'
+
+ const response = await request<
+ EstimateResponse | ReverseEstimateResponse | LimitEstimateResponse
+ >({
+ method: 'post',
+ url: `${baseUrl}${apiPaths[kind]}`,
+ headers,
+ data: transformers[kind].request(body),
+ })
+
+ if (isLeft(response)) return response
+
+ return freeze(
+ {
+ tag: 'right',
+ value: {
+ status: response.value.status,
+ data: transformers[kind].response(response.value.data as any),
+ },
+ },
+ true,
+ )
+ },
+
+ async create(body: Swap.CreateRequest) {
+ const kind: 'build' | 'limitBuild' =
+ body.wantedPrice !== undefined ? 'limitBuild' : 'build'
+
+ const response = await request({
+ method: 'post',
+ url: `${baseUrl}${apiPaths[kind]}`,
+ headers,
+ data: transformers[kind].request(body),
+ })
+
+ if (isLeft(response)) return response
+
+ return freeze(
+ {
+ tag: 'right',
+ value: {
+ status: response.value.status,
+ data: transformers[kind].response(response.value.data as any),
+ },
+ },
+ true,
+ )
+ },
+
+ async cancel(body: Swap.CancelRequest) {
+ const response = await request({
+ method: 'post',
+ url: `${baseUrl}${apiPaths.cancel}`,
+ headers,
+ data: transformers.cancel.request(body),
+ })
+
+ if (isLeft(response)) return response
+
+ return freeze(
+ {
+ tag: 'right',
+ value: {
+ status: response.value.status,
+ data: transformers.cancel.response(response.value.data),
+ },
+ },
+ true,
+ )
+ },
+ },
+ true,
+ )
+}
+
+const baseUrls = {
+ [Chain.Network.Mainnet]: 'https://api-us.dexhunterv3.app',
+} as const
+
+const apiPaths = {
+ tokens: '/swap/tokens', // GET
+ cancel: '/swap/cancel', // POST
+ estimate: '/swap/estimate', // POST
+ limitBuild: '/swap/limit/build', // POST
+ limitEstimate: '/swap/limit/estimate', // POST
+ orders: ({address}: {address: string}) => `/swap/orders/${address}`, // GET
+ reverseEstimate: '/swap/reverseEstimate', // POST
+ build: '/swap/build', // POST
+ sign: '/swap/sign', // POST
+ averagePrice: ({
+ tokenInId,
+ tokenOutId,
+ }: {
+ tokenInId: string
+ tokenOutId: string
+ }) => `/swap/averagePrice/${tokenInId}/${tokenOutId}`, // GET
+ wallet: '/swap/wallet', // POST
+ charts: '/charts', // POST
+ dcaCancel: '/dca/cancel', // POST
+ dcaCreate: '/dca/create', // POST
+ dcaEstimate: '/dca/estimate', // POST
+ dcaByAdress: ({address}: {address: string}) => `/dca/${address}`, // GET
+ markingSubmit: '/marking/submit', // POST
+} as const
diff --git a/packages/swap/src/adapters/api/dexhunter/transformers.ts b/packages/swap/src/adapters/api/dexhunter/transformers.ts
new file mode 100644
index 0000000000..82ad3ca12f
--- /dev/null
+++ b/packages/swap/src/adapters/api/dexhunter/transformers.ts
@@ -0,0 +1,360 @@
+import {Portfolio, Swap} from '@yoroi/types'
+import {
+ BuildRequest,
+ BuildResponse,
+ CancelRequest,
+ CancelResponse,
+ EstimateRequest,
+ EstimateResponse,
+ LimitBuildRequest,
+ LimitBuildResponse,
+ LimitEstimateRequest,
+ LimitEstimateResponse,
+ OrdersResponse,
+ ReverseEstimateRequest,
+ ReverseEstimateResponse,
+ SignRequest,
+ SignResponse,
+ Split,
+ TokensResponse,
+} from './types'
+import {isPrimaryToken} from '@yoroi/portfolio'
+import {DexhunterApiConfig} from './api-maker'
+
+const tokenIdToDexhunter = (tokenId: Portfolio.Token.Id) =>
+ isPrimaryToken(tokenId) ? 'ADA' : tokenId.replace('.', '')
+
+const transformSplit = ({
+ amount_in = 0,
+ batcher_fee = 0,
+ deposits = 0,
+ dex = '',
+ expected_output = 0,
+ expected_output_without_slippage = 0,
+ fee = 0,
+ final_price = 0,
+ initial_price = 0,
+ pool_fee = 0,
+ pool_id = '',
+ price_distortion = 0,
+ price_impact = 0,
+}: Split): Swap.Split => ({
+ amountIn: amount_in,
+ batcherFee: batcher_fee,
+ deposits,
+ dex,
+ expectedOutput: expected_output,
+ expectedOutputWithoutSlippage: expected_output_without_slippage,
+ fee,
+ finalPrice: final_price,
+ initialPrice: initial_price,
+ poolFee: pool_fee,
+ poolId: pool_id,
+ priceDistortion: price_distortion,
+ priceImpact: price_impact,
+})
+export const transformersMaker = ({
+ primaryTokenInfo,
+ address,
+}: DexhunterApiConfig) => {
+ const tokenIdFromDexhunter = (tokenId: string): Portfolio.Token.Id =>
+ tokenId ===
+ '000000000000000000000000000000000000000000000000000000006c6f76656c616365'
+ ? primaryTokenInfo.id
+ : `${tokenId.slice(0, 56)}.${tokenId.slice(56)}`
+
+ return {
+ tokens: {
+ response: (res: TokensResponse): Array =>
+ res.map(
+ ({
+ token_id,
+ token_decimals,
+ token_ascii,
+ ticker,
+ is_verified,
+ supply,
+ creation_date,
+ price,
+ }) => {
+ if (
+ token_id ===
+ '000000000000000000000000000000000000000000000000000000006c6f76656c616365'
+ )
+ return primaryTokenInfo
+ return {
+ id: tokenIdFromDexhunter(token_id),
+ type: Portfolio.Token.Type.FT,
+ nature: Portfolio.Token.Nature.Secondary,
+ decimals: token_decimals ?? 0,
+ ticker: ticker ?? '',
+ name: token_ascii ?? '',
+ symbol: ticker ?? '',
+ status: is_verified
+ ? Portfolio.Token.Status.Valid
+ : Portfolio.Token.Status.Unknown,
+ application: Portfolio.Token.Application.General,
+ tag: '',
+ reference: '',
+ fingerprint: '',
+ description: `${price}, ${supply}, ${creation_date}`,
+ website: '',
+ originalImage: '',
+ }
+ },
+ ),
+ },
+ orders: {
+ response: (res: OrdersResponse): Array =>
+ res.map(
+ ({
+ _id,
+ actual_out_amount = 0,
+ amount_in = 0,
+ dex = '',
+ expected_out_amount = 0,
+ is_dexhunter = false,
+ last_update,
+ status = '',
+ submission_time,
+ token_id_in = '',
+ token_id_out = '',
+ tx_hash = '',
+ update_tx_hash = '',
+ output_index,
+ }) => ({
+ aggregator: is_dexhunter
+ ? Swap.Aggregator.Dexhunter
+ : Swap.Aggregator.Muesliswap,
+ dex,
+ placedAt: new Date(submission_time).getTime(),
+ lastUpdate: new Date(last_update).getTime(),
+ status,
+ tokenIn: tokenIdFromDexhunter(token_id_in),
+ tokenOut: tokenIdFromDexhunter(token_id_out),
+ amountIn: amount_in,
+ actualAmountOut: actual_out_amount,
+ expectedAmountOut: expected_out_amount,
+ txHash: tx_hash,
+ outputIndex: output_index,
+ updateTxHash: update_tx_hash,
+ customId: _id,
+ }),
+ ),
+ },
+ cancel: {
+ request: ({order}: Swap.CancelRequest): CancelRequest => ({
+ address,
+ order_id: order.customId,
+ }),
+ response: ({
+ additional_cancellation_fee,
+ cbor = '',
+ }: CancelResponse): Swap.CancelResponse => ({
+ cbor,
+ additionalCancellationFee: additional_cancellation_fee,
+ }),
+ },
+ estimate: {
+ request: ({
+ amountIn,
+ blacklistedDexes,
+ slippage,
+ tokenIn,
+ tokenOut,
+ }: Swap.EstimateRequest): EstimateRequest => ({
+ amount_in: amountIn,
+ blacklisted_dexes: blacklistedDexes,
+ slippage,
+ token_in: tokenIdToDexhunter(tokenIn),
+ token_out: tokenIdToDexhunter(tokenOut),
+ }),
+ response: ({
+ batcher_fee = 0,
+ deposits = 0,
+ dexhunter_fee = 0,
+ net_price = 0,
+ partner_fee = 0,
+ splits,
+ total_fee = 0,
+ total_output = 0,
+ total_output_without_slippage = 0,
+ }: EstimateResponse): Swap.EstimateResponse => ({
+ splits: splits?.map(transformSplit) ?? [],
+ batcherFee: batcher_fee,
+ deposits,
+ aggregatorFee: dexhunter_fee,
+ frontendFee: partner_fee,
+ netPrice: net_price,
+ totalFee: total_fee,
+ totalOutput: total_output,
+ totalOutputWithoutSlippage: total_output_without_slippage,
+ }),
+ },
+ reverseEstimate: {
+ request: ({
+ amountOut,
+ blacklistedDexes,
+ slippage,
+ tokenIn,
+ tokenOut,
+ }: Swap.EstimateRequest): ReverseEstimateRequest => ({
+ amount_out: amountOut,
+ blacklisted_dexes: blacklistedDexes,
+ slippage,
+ token_in: tokenIdToDexhunter(tokenIn),
+ token_out: tokenIdToDexhunter(tokenOut),
+ }),
+ response: ({
+ batcher_fee = 0,
+ deposits = 0,
+ dexhunter_fee = 0,
+ net_price = 0,
+ partner_fee = 0,
+ splits,
+ total_fee = 0,
+ total_input = 0,
+ total_output = 0,
+ }: ReverseEstimateResponse): Swap.EstimateResponse => ({
+ splits: splits?.map(transformSplit) ?? [],
+ batcherFee: batcher_fee,
+ deposits,
+ aggregatorFee: dexhunter_fee,
+ frontendFee: partner_fee,
+ netPrice: net_price,
+ totalFee: total_fee,
+ totalOutput: total_output,
+ totalInput: total_input,
+ }),
+ },
+ limitEstimate: {
+ request: ({
+ amountIn,
+ blacklistedDexes,
+ dex,
+ multiples,
+ tokenIn,
+ tokenOut,
+ wantedPrice,
+ }: Swap.EstimateRequest): LimitEstimateRequest => ({
+ amount_in: amountIn,
+ blacklisted_dexes: blacklistedDexes,
+ dex: dex,
+ multiples,
+ token_in: tokenIdToDexhunter(tokenIn),
+ token_out: tokenIdToDexhunter(tokenOut),
+ wanted_price: wantedPrice,
+ }),
+ response: ({
+ batcher_fee = 0,
+ deposits = 0,
+ dexhunter_fee = 0,
+ net_price = 0,
+ partner_fee = 0,
+ splits,
+ total_fee = 0,
+ total_input = 0,
+ total_output = 0,
+ }: LimitEstimateResponse): Swap.EstimateResponse => ({
+ splits: splits?.map(transformSplit) ?? [],
+ batcherFee: batcher_fee,
+ deposits,
+ aggregatorFee: dexhunter_fee,
+ frontendFee: partner_fee,
+ netPrice: net_price,
+ totalFee: total_fee,
+ totalOutput: total_output,
+ totalInput: total_input,
+ }),
+ },
+ limitBuild: {
+ request: ({
+ amountIn,
+ blacklistedDexes,
+ dex,
+ multiples,
+ tokenIn,
+ tokenOut,
+ wantedPrice,
+ }: Swap.CreateRequest): LimitBuildRequest => ({
+ amount_in: amountIn,
+ blacklisted_dexes: blacklistedDexes,
+ buyer_address: address,
+ dex: dex,
+ multiples,
+ token_in: tokenIdToDexhunter(tokenIn),
+ token_out: tokenIdToDexhunter(tokenOut),
+ wanted_price: wantedPrice,
+ }),
+ response: ({
+ cbor = '',
+ batcher_fee = 0,
+ deposits = 0,
+ dexhunter_fee = 0,
+ partner_fee = 0,
+ splits,
+ totalFee = 0,
+ total_input = 0,
+ total_output = 0,
+ }: LimitBuildResponse): Swap.CreateResponse => ({
+ cbor,
+ splits: splits?.map(transformSplit) ?? [],
+ batcherFee: batcher_fee,
+ deposits,
+ aggregatorFee: dexhunter_fee,
+ frontendFee: partner_fee,
+ totalFee: totalFee,
+ totalInput: total_input,
+ totalOutput: total_output,
+ }),
+ },
+ build: {
+ request: ({
+ amountIn,
+ blacklistedDexes,
+ slippage = 0,
+ tokenIn,
+ tokenOut,
+ }: Swap.CreateRequest): BuildRequest => ({
+ amount_in: amountIn,
+ blacklisted_dexes: blacklistedDexes,
+ buyer_address: address,
+ slippage,
+ token_in: tokenIdToDexhunter(tokenIn),
+ token_out: tokenIdToDexhunter(tokenOut),
+ }),
+ response: ({
+ cbor = '',
+ batcher_fee = 0,
+ deposits = 0,
+ dexhunter_fee = 0,
+ net_price = 0,
+ partner_fee = 0,
+ splits,
+ total_fee = 0,
+ total_input = 0,
+ total_output = 0,
+ total_output_without_slippage = 0,
+ }: BuildResponse): Swap.CreateResponse => ({
+ cbor,
+ splits: splits?.map(transformSplit) ?? [],
+ batcherFee: batcher_fee,
+ deposits,
+ aggregatorFee: dexhunter_fee,
+ frontendFee: partner_fee,
+ netPrice: net_price,
+ totalFee: total_fee,
+ totalInput: total_input,
+ totalOutput: total_output,
+ totalOutputWithoutSlippage: total_output_without_slippage,
+ }),
+ },
+ sign: {
+ request: ({signatures, txCbor}: any): SignRequest => ({
+ Signatures: signatures,
+ txCbor,
+ }),
+ response: ({cbor, strat_id}: SignResponse) => ({cbor, stratId: strat_id}),
+ },
+ } as const
+}
diff --git a/packages/swap/src/adapters/api/dexhunter/types.ts b/packages/swap/src/adapters/api/dexhunter/types.ts
new file mode 100644
index 0000000000..65894b4ec2
--- /dev/null
+++ b/packages/swap/src/adapters/api/dexhunter/types.ts
@@ -0,0 +1,210 @@
+export type TokensResponse = Array<{
+ token_id: string
+ token_decimals: number
+ token_policy: string
+ token_ascii: string
+ ticker: string
+ is_verified: boolean
+ supply: number
+ creation_date: string
+ price: number
+}>
+
+export type OrdersResponse = Array<{
+ _id?: string
+ actual_out_amount?: number
+ amount_in?: number
+ batcher_fee?: number
+ deposit?: number
+ dex?: string
+ expected_out_amount?: number
+ is_dexhunter?: boolean
+ is_oor?: boolean
+ is_stop_loss?: boolean
+ last_update: string
+ output_index?: number
+ status?: string
+ submission_time: string
+ token_id_in?: string
+ token_id_out?: string
+ tx_hash?: string
+ update_tx_hash?: string
+ user_address?: string
+ user_stake?: string
+}>
+
+export type CancelRequest = {
+ address?: string
+ order_id?: string
+}
+
+export type CancelResponse = {
+ additional_cancellation_fee?: number
+ cbor?: string
+}
+
+export type Split = {
+ amount_in?: number
+ batcher_fee?: number
+ deposits?: number
+ dex?: string
+ expected_output?: number
+ expected_output_without_slippage?: number
+ fee?: number
+ final_price?: number
+ initial_price?: number
+ pool_fee?: number
+ pool_id?: string
+ price_distortion?: number
+ price_impact?: number
+}
+
+export type EstimateRequest = {
+ amount_in?: number
+ blacklisted_dexes?: string[]
+ slippage?: number
+ token_in?: string
+ token_out?: string
+}
+
+export type EstimateResponse = {
+ average_price?: number
+ batcher_fee?: number
+ communications?: string[]
+ deposits?: number
+ dexhunter_fee?: number
+ net_price?: number
+ net_price_reverse?: number
+ partner_code?: string
+ partner_fee?: number
+ possible_routes?: {
+ [key: string]: number
+ }
+ splits?: Split[]
+ total_fee?: number
+ total_output?: number
+ total_output_without_slippage?: number
+}
+
+export type ReverseEstimateRequest = {
+ amount_out?: number
+ blacklisted_dexes?: string[]
+ slippage: number
+ token_in: string
+ token_out: string
+}
+
+export type ReverseEstimateResponse = {
+ average_price?: number
+ batcher_fee?: number
+ communications?: string[]
+ deposits?: number
+ dexhunter_fee?: number
+ net_price?: number
+ net_price_reverse?: number
+ partner_fee?: number
+ possible_routes?: {
+ [key: string]: number
+ }
+ splits?: Split[]
+ total_fee?: number
+ total_input?: number
+ total_input_without_slippage?: number
+ total_output?: number
+}
+
+export type LimitEstimateRequest = {
+ amount_in?: number
+ blacklisted_dexes?: string[]
+ dex?: string
+ multiples?: number
+ token_in?: string
+ token_out?: string
+ wanted_price?: number
+}
+
+export type LimitEstimateResponse = {
+ batcher_fee?: number
+ blacklisted_dexes?: string[]
+ deposits?: number
+ dexhunter_fee?: number
+ net_price?: number
+ partner?: string
+ partner_fee?: number
+ possible_routes?: {
+ [key: string]: string
+ }
+ splits?: Split[]
+ total_fee?: number
+ total_input?: number
+ total_output?: number
+}
+
+export type LimitBuildRequest = {
+ amount_in?: number
+ blacklisted_dexes?: string[]
+ buyer_address?: string
+ dex?: string
+ multiples?: number
+ token_in?: string
+ token_out?: string
+ wanted_price?: number
+}
+
+export type LimitBuildResponse = {
+ batcher_fee?: number
+ cbor?: string
+ deposits?: number
+ dexhunter_fee?: number
+ partner?: string
+ partner_fee?: number
+ possible_routes?: {
+ [key: string]: string
+ }
+ splits?: Split[]
+ totalFee?: number
+ total_input?: number
+ total_output?: number
+}
+
+export type BuildRequest = {
+ amount_in: number
+ blacklisted_dexes?: string[]
+ buyer_address: string
+ tx_optimization?: boolean
+ slippage: number
+ token_in: string
+ token_out: string
+}
+
+export type BuildResponse = {
+ average_price?: number
+ batcher_fee?: number
+ cbor?: string
+ communications?: string[]
+ deposits?: number
+ dexhunter_fee?: number
+ net_price?: number
+ net_price_reverse?: number
+ partner_code?: string
+ partner_fee?: number
+ possible_routes?: {
+ [key: string]: number
+ }
+ splits?: Split[]
+ total_fee?: number
+ total_input?: number
+ total_input_without_slippage?: number
+ total_output?: number
+ total_output_without_slippage?: number
+}
+
+export type SignRequest = {
+ Signatures?: string
+ txCbor?: string
+}
+
+export type SignResponse = {
+ cbor?: string
+ strat_id?: string
+}
diff --git a/packages/swap/src/adapters/api/dexhunter/unused-types.ts b/packages/swap/src/adapters/api/dexhunter/unused-types.ts
new file mode 100644
index 0000000000..77887c23c8
--- /dev/null
+++ b/packages/swap/src/adapters/api/dexhunter/unused-types.ts
@@ -0,0 +1,143 @@
+/**
+ * OrdersRequest seems unused in v3 as orders endpoint accepts no body
+ */
+export type OrdersRequest = {
+ filters?: {
+ filterType?:
+ | 'TOKENID'
+ | 'STATUS'
+ | 'TXTYPE'
+ | 'TIMESTART'
+ | 'TIMEEND'
+ | 'DEXNAME'
+ | 'SEARCH'
+ | 'ADDRESS'
+ | 'MINAMOUNT'
+ | 'MAXAMOUNT'
+ | 'TXHASH'
+ | 'OWNED'
+ values?: string[] // Ex. for status: PENDING | LIMIT | COMPLETED | CANCELLED
+ }[]
+ orderSorts?: 'AMOUNTIN' | 'DATE'
+ page?: number
+ perPage?: number
+ sortDirection?: 'ASC' | 'DESC'
+}
+
+export type AveragePriceResponse = {
+ averagePrice: number // (Ada / Token) === price_ab
+ price_ab: number // Ada / Token
+ price_ba: number // Token / Ada
+}
+
+export type WalletInfoRequest = {
+ addresses?: string[]
+}
+
+export type WalletInfoResponse = {
+ cardano?: {
+ [key: string]: number
+ }
+ tokens?: UserToken[]
+}
+
+export type UserToken = {
+ ada_value?: number
+ amount?: number
+ ticker?: string
+ token_ascii?: string
+ token_id?: string
+}
+
+export type OHLC = {
+ close?: number
+ high?: number
+ low?: number
+ open?: number
+ timestamp?: string
+ volume?: number
+}
+
+export type Period =
+ | '1min'
+ | '5min'
+ | '15min'
+ | '30min'
+ | '1hour'
+ | '4hour'
+ | '1day'
+
+export type ChartRequest = {
+ from?: number
+ isLast?: boolean
+ period?: Period
+ to?: number
+ tokenIn?: string
+ tokenOut?: string
+}
+
+export type ChartResponse = {
+ data?: OHLC[]
+ period?: Period
+}
+
+export type CancelDcaRequest = {
+ dca_id?: string
+ user_address?: string
+}
+
+export type CancelDcaResponse = {
+ [key: string]: string
+}
+
+export type CreateDcaRequest = {
+ amount_in?: number
+ cycles?: number
+ dex_allowlist?: string[]
+ interval?: DcaInterval
+ interval_length?: number
+ token_in?: string
+ token_out?: string
+ user_address?: string
+}
+
+export type CreateDcaResponse = {
+ amount_ada_in?: number
+ amount_token_in?: number
+ batchers_deposit?: number
+ cbor?: string
+ dca_id?: string
+ dh_fee?: number
+ tx_fees_deposit?: number
+}
+
+export type DcaInterval =
+ | 'minutely'
+ | 'hourly'
+ | 'daily'
+ | 'weekly'
+ | 'monthly'
+ | 'quarterly'
+
+export type DcaResponse = {
+ amount_dcad?: number
+ creation_tx?: string
+ current_slot?: number
+ dca_amount?: number
+ id?: string
+ interval?: number
+ last_execution?: string
+ next_execution?: string
+ remaining_cycles?: number
+ status?: 'active' | 'error' | 'done'
+ token_in?: string
+ token_out?: string
+ total_dca?: number
+}
+
+export type MarkingType = 'LIMIT' | 'STOP_LOSS' | 'DCA' | 'SWAP'
+export type MarkingRequest = {
+ cbor?: string
+ order_type?: MarkingType
+ tx_hash?: string
+}
diff --git a/packages/swap/src/adapters/api/muesliswap/api-maker.ts b/packages/swap/src/adapters/api/muesliswap/api-maker.ts
new file mode 100644
index 0000000000..179a063a03
--- /dev/null
+++ b/packages/swap/src/adapters/api/muesliswap/api-maker.ts
@@ -0,0 +1,307 @@
+import {FetchData, fetchData, isLeft} from '@yoroi/common'
+import {Api, App, Chain, Portfolio, Swap} from '@yoroi/types'
+import {freeze} from 'immer'
+import {memoize} from 'lodash-es'
+import {
+ CancelRequest,
+ CancelResponse,
+ ConstructSwapDatumResponse,
+ LiquidityPoolResponse,
+ OrdersAggregatorResponse,
+ OrdersHistoryResponse,
+ TokensResponse,
+} from './types'
+import {transformersMaker} from './transformers'
+import {estimateCalculation} from './calculations'
+
+export type MuesliswapApiConfig = {
+ frontendFeeTiers: ReadonlyArray
+ lpTokenHeld?: number
+ addressHex: string
+ address: string
+ primaryTokenInfo: Portfolio.Token.Info
+ stakingKey: string
+ network: Chain.SupportedNetworks
+ request?: FetchData
+}
+export const muesliswapApiMaker = (
+ config: MuesliswapApiConfig,
+): Readonly => {
+ const {
+ frontendFeeTiers,
+ lpTokenHeld,
+ stakingKey,
+ addressHex,
+ primaryTokenInfo,
+ network,
+ request = fetchData,
+ } = config
+
+ if (network !== Chain.Network.Mainnet)
+ return new Proxy(
+ {},
+ {
+ get() {
+ return () =>
+ freeze(
+ {
+ tag: 'left',
+ error: {
+ status: -3,
+ message: 'Muesliswap api only works on mainnet',
+ },
+ },
+ true,
+ )
+ },
+ },
+ ) as Swap.Api
+
+ const headers = {
+ 'Content-Type': 'application/json',
+ 'Accept': 'application/json',
+ }
+
+ const transformers = transformersMaker(config)
+
+ // Asking for pools on every amount change would be bad UI and third party API spam
+ const getLiquidityPools = memoize(
+ async (body: Swap.EstimateRequest) => {
+ const params = transformers.liquidityPools.request(body)
+
+ const response = await request(
+ {
+ method: 'get',
+ url: apiUrls.liquidityPools,
+ headers,
+ },
+ {
+ params,
+ },
+ )
+
+ if (isLeft(response)) return response
+
+ return freeze(
+ {
+ tag: 'right' as const,
+ value: {
+ status: response.value.status,
+ data: transformers.liquidityPools.response(
+ response.value.data,
+ body,
+ ),
+ },
+ },
+ true,
+ )
+ },
+ ({tokenIn, tokenOut, dex, blacklistedDexes}) =>
+ [
+ new Date().getMinutes(), // cache every minute
+ tokenIn,
+ tokenOut,
+ dex,
+ blacklistedDexes?.join(),
+ ].join('_'),
+ )
+
+ return freeze(
+ {
+ async tokens() {
+ const response = await request({
+ method: 'get',
+ url: apiUrls.tokens,
+ headers,
+ })
+
+ if (isLeft(response)) return response
+
+ return freeze(
+ {
+ tag: 'right',
+ value: {
+ status: response.value.status,
+ data: transformers.tokens.response(response.value.data),
+ },
+ },
+ true,
+ )
+ },
+
+ async orders() {
+ const [historyResponse, aggregatorResponse] = await Promise.all([
+ request(
+ {
+ method: 'get',
+ url: apiUrls.ordersHistory,
+ headers,
+ },
+ {
+ params: {
+ 'stake-key-hash': stakingKey,
+ },
+ },
+ ),
+ request(
+ {
+ method: 'get',
+ url: apiUrls.ordersAggregator,
+ headers,
+ },
+ {
+ params: {
+ wallet: addressHex,
+ },
+ },
+ ),
+ ])
+
+ if (isLeft(historyResponse)) return historyResponse
+ if (isLeft(aggregatorResponse)) return aggregatorResponse
+
+ return freeze(
+ {
+ tag: 'right',
+ value: {
+ status: 200,
+ data: [
+ ...transformers.ordersHistory.response(
+ historyResponse.value.data,
+ ),
+ ...transformers.ordersAggregator.response(
+ aggregatorResponse.value.data,
+ ),
+ ],
+ },
+ },
+ true,
+ )
+ },
+
+ async estimate(body: Swap.EstimateRequest) {
+ // This cache is very dumb, clear on ocasion so it doesn't accumulate too many entries
+ if (body.amountIn === 0) getLiquidityPools.cache.clear?.()
+
+ const response = await getLiquidityPools(body)
+
+ if (isLeft(response)) return response
+
+ try {
+ return freeze(
+ {
+ tag: 'right',
+ value: {
+ status: response.value.status,
+ data: estimateCalculation(
+ response.value.data,
+ body,
+ primaryTokenInfo,
+ frontendFeeTiers,
+ lpTokenHeld,
+ ),
+ },
+ },
+ true,
+ )
+ } finally {
+ return freeze(
+ {
+ tag: 'left',
+ error: {
+ status: -3,
+ message: 'No liquidity pools satisfy the estimate requirements',
+ responseData: response.value.data,
+ },
+ },
+ true,
+ )
+ }
+ },
+
+ async create(body: Swap.CreateRequest) {
+ const estimateResponse: Api.Response =
+ await this.estimate({...body, slippage: body.slippage ?? 0})
+
+ if (isLeft(estimateResponse)) return estimateResponse
+
+ const lastEstimate = estimateResponse.value.data
+
+ const params = transformers.constructSwapDatum.request(
+ body,
+ lastEstimate.splits[0]!,
+ )
+
+ const response = await request(
+ {
+ method: 'get',
+ url: apiUrls.constructSwapDatum,
+ headers,
+ },
+ {
+ params,
+ },
+ )
+
+ if (isLeft(response)) return response
+
+ return freeze(
+ {
+ tag: 'right',
+ value: {
+ status: response.value.status,
+ data: transformers.constructSwapDatum.response(
+ response.value.data,
+ lastEstimate,
+ ),
+ },
+ },
+ true,
+ )
+ },
+
+ async cancel(body: Swap.CancelRequest) {
+ const params: CancelRequest = transformers.cancel.request(body)
+
+ const response = await request(
+ {
+ method: 'post',
+ url: apiUrls.cancel,
+ headers,
+ },
+ {
+ params,
+ },
+ )
+
+ if (isLeft(response)) return response
+
+ return freeze(
+ {
+ tag: 'right',
+ value: {
+ status: response.value.status,
+ data: transformers.cancel.response(response.value.data),
+ },
+ },
+ true,
+ )
+ },
+ },
+ true,
+ )
+}
+
+const apiUrls = {
+ tokens: 'https://api.muesliswap.com/list',
+ ordersHistory: 'https://api.muesliswap.com/orders/v3/history',
+ ordersAggregator: 'https://api.muesliswap.com/orders/aggregator',
+ liquidityPools: 'https://api.muesliswap.com/liquidity/pools',
+ constructSwapDatum: 'https://aggregator.muesliswap.com/constructSwapDatum',
+ cancel: 'https://aggregator.muesliswap.com/cancelSwapTransaction',
+} as const
+
+export const milkTokenId =
+ 'afbe91c0b44b3040e360057bf8354ead8c49c4979ae6ab7c4fbdc9eb.4d494c4b7632'
+export const oldMilkTokenId =
+ '8a1cfae21368b8bebbbed9800fec304e95cce39a2a57dc35e2e3ebaa.4d494c4b'
diff --git a/packages/swap/src/adapters/api/muesliswap/calculations.ts b/packages/swap/src/adapters/api/muesliswap/calculations.ts
new file mode 100644
index 0000000000..410bdaa968
--- /dev/null
+++ b/packages/swap/src/adapters/api/muesliswap/calculations.ts
@@ -0,0 +1,260 @@
+import {App, Portfolio, Swap} from '@yoroi/types'
+import {Pools} from './types'
+
+export const estimateCalculation = (
+ pools: Pools,
+ estimate: Swap.EstimateRequest,
+ primaryTokenInfo: Portfolio.Token.Info,
+ frontendFeeTiers: ReadonlyArray,
+ lpTokenHeld?: number,
+): Swap.EstimateResponse => {
+ const totalInSupply = pools.reduce(
+ (total, pool) => total + pool.tokenInSupply,
+ 0,
+ )
+ const totalOutSupply = pools.reduce(
+ (total, pool) => total + pool.tokenOutSupply,
+ 0,
+ )
+
+ const marketPrice = totalInSupply / totalOutSupply
+
+ const availableSplits: Array = pools
+ .map((pool) => {
+ if (pool.tokenInSupply <= 0 || pool.tokenOutSupply <= 0) return null
+
+ const amountIn =
+ estimate.amountIn ??
+ fromBaseUnits(
+ getAmountIn({
+ tokenInSupply: pool.tokenInSupply,
+ tokenOutSupply: pool.tokenOutSupply,
+ fee: pool.fee,
+ amountOut: toBaseUnits(estimate.amountOut, pool.tokenOutDecimals),
+ wantedPrice: estimate.wantedPrice,
+ }),
+ pool.tokenInDecimals,
+ )
+
+ const amountOut =
+ estimate.amountOut ??
+ fromBaseUnits(
+ getAmountOut({
+ tokenInSupply: pool.tokenInSupply,
+ tokenOutSupply: pool.tokenOutSupply,
+ fee: pool.fee,
+ amountIn: toBaseUnits(estimate.amountIn, pool.tokenInDecimals),
+ wantedPrice: estimate.wantedPrice,
+ }),
+ pool.tokenOutDecimals,
+ )
+
+ const amountOutWithSlippage = fromBaseUnits(
+ withSlippage(
+ toBaseUnits(amountOut, pool.tokenOutDecimals),
+ estimate.wantedPrice === undefined ? estimate.slippage : 0,
+ ),
+ pool.tokenOutDecimals,
+ )
+
+ // No multiple splits supported yet, so if there's no supply for 1, discard pool
+ if (amountOutWithSlippage > pool.tokenOutSupply) return null
+
+ const initialPrice = pool.tokenInSupply / pool.tokenOutSupply
+
+ const finalPrice =
+ (pool.tokenInSupply + amountIn) / (pool.tokenOutSupply - amountOut)
+
+ const priceImpact = 100 * ((finalPrice - initialPrice) / initialPrice)
+
+ const priceDistortion = 100 * ((finalPrice - marketPrice) / marketPrice)
+
+ const batcherFee = fromBaseUnits(
+ pool.batcherFee,
+ primaryTokenInfo.decimals,
+ )
+
+ const deposits = fromBaseUnits(pool.deposit, primaryTokenInfo.decimals)
+
+ return {
+ amountIn,
+ batcherFee,
+ deposits,
+ dex: pool.provider,
+ expectedOutput: amountOutWithSlippage,
+ expectedOutputWithoutSlippage: amountOut,
+ fee: deposits + batcherFee,
+ finalPrice,
+ initialPrice,
+ poolFee: pool.fee,
+ poolId: pool.poolId,
+ priceDistortion,
+ priceImpact,
+ }
+ })
+ .filter((split) => split !== null)
+
+ if (availableSplits.length === 0) throw new Error()
+
+ const bestSplit = availableSplits
+ .sort((a, b) => a.priceDistortion - b.priceDistortion)
+ .reduce((best, split) => {
+ if (estimate.amountOut === undefined) {
+ return (best?.expectedOutput ?? 0) > split.expectedOutput ? best : split
+ }
+ return (best?.amountIn ?? Infinity) < split.amountIn ? best : split
+ }, availableSplits[0]!)
+
+ const netPrice =
+ estimate.wantedPrice ??
+ bestSplit.amountIn / bestSplit.expectedOutputWithoutSlippage
+
+ const pool = pools.find(({poolId}) => bestSplit.poolId === poolId)
+ const ptAmount = Math.max(
+ (pool?.tokenInPtPrice ?? 0) * bestSplit.amountIn,
+ (pool?.tokenOutPtPrice ?? 0) * bestSplit.expectedOutput,
+ )
+
+ // TODO check units
+ const frontendFee = getFrontendFee({
+ ptAmount,
+ frontendFeeTiers,
+ lpTokenHeld,
+ })
+
+ const aggregatorFee = 0
+
+ return {
+ splits: [bestSplit],
+ batcherFee: bestSplit.batcherFee,
+ deposits: bestSplit.deposits,
+ aggregatorFee,
+ frontendFee,
+ netPrice,
+ totalFee: bestSplit.fee + aggregatorFee + frontendFee,
+ totalOutput: bestSplit.expectedOutput,
+ totalOutputWithoutSlippage:
+ estimate.amountIn === undefined
+ ? bestSplit.expectedOutputWithoutSlippage
+ : undefined,
+ totalInput:
+ estimate.amountIn === undefined ? bestSplit.amountIn : undefined,
+ }
+}
+
+const getAmountIn = ({
+ tokenInSupply,
+ tokenOutSupply,
+ fee,
+ amountOut,
+ wantedPrice,
+}: {
+ tokenInSupply: number
+ tokenOutSupply: number
+ fee: number
+ amountOut: number
+ wantedPrice?: number
+}): number => {
+ if (amountOut <= 0) return 0
+
+ if (wantedPrice !== undefined) return Math.ceil(amountOut * wantedPrice)
+
+ const feeFactor = BigInt(100 * 1000) - BigInt(fee * 1000)
+
+ const inSupply = BigInt(tokenInSupply)
+
+ const outSupply = BigInt(tokenOutSupply)
+
+ const finalOutSupply =
+ outSupply -
+ (outSupply > amountOut ? BigInt(amountOut) : outSupply - BigInt(1))
+
+ return Number(
+ ceilDivision(
+ (ceilDivision(outSupply * inSupply + finalOutSupply, finalOutSupply) -
+ inSupply) *
+ BigInt(100 * 1000),
+ feeFactor,
+ ),
+ )
+}
+
+const getAmountOut = ({
+ tokenInSupply,
+ tokenOutSupply,
+ fee,
+ amountIn,
+ wantedPrice,
+}: {
+ tokenInSupply: number
+ tokenOutSupply: number
+ fee: number
+ amountIn: number
+ wantedPrice?: number
+}): number => {
+ if (amountIn <= 0) return 0
+
+ if (wantedPrice !== undefined) return Math.floor(amountIn / wantedPrice)
+
+ const bigAmountIn = BigInt(amountIn)
+
+ const feeFactor = ceilDivision(
+ BigInt(fee * 1000) * bigAmountIn,
+ BigInt(100 * 1000),
+ )
+
+ const inSupply = BigInt(tokenInSupply)
+
+ const outSupply = BigInt(tokenOutSupply)
+
+ return Number(
+ outSupply -
+ ceilDivision(outSupply * inSupply, outSupply + bigAmountIn - feeFactor),
+ )
+}
+
+export const withSlippage = (amount: number, slippage: number) => {
+ const initialAmount = BigInt(amount)
+
+ const slippageAmount = ceilDivision(
+ BigInt(Math.floor(10_000 * slippage)) * initialAmount,
+ BigInt(100 * 10_000),
+ )
+
+ return Number(initialAmount - slippageAmount)
+}
+
+export const getFrontendFee = ({
+ lpTokenHeld,
+ ptAmount,
+ frontendFeeTiers,
+}: {
+ frontendFeeTiers: ReadonlyArray
+ lpTokenHeld?: number
+ ptAmount: number
+}): number => {
+ // identify the discount
+ const discountTier = frontendFeeTiers.find(
+ (tier) =>
+ (lpTokenHeld ?? 0) >= Number(tier.secondaryTokenBalanceThreshold) &&
+ ptAmount >= Number(tier.primaryTokenValueThreshold),
+ )
+
+ return (
+ ptAmount * (discountTier?.variableFeeMultiplier ?? 0) +
+ (Number(discountTier?.fixedFee) ?? 0)
+ )
+}
+
+const ceilDivision = (dividend: bigint, divisor: bigint): bigint => {
+ if (dividend <= 0n || divisor <= 0n) return 0n
+ const adjustedDivisor = divisor - 1n
+
+ return (dividend + adjustedDivisor) / divisor
+}
+
+const toBaseUnits = (amount: number, decimals: number) =>
+ Number((amount * Math.pow(10, decimals)).toFixed(0))
+
+const fromBaseUnits = (amount: number, decimals: number) =>
+ Number((amount * Math.pow(10, -1 * decimals)).toFixed(decimals))
diff --git a/packages/swap/src/adapters/api/muesliswap/transformers.ts b/packages/swap/src/adapters/api/muesliswap/transformers.ts
new file mode 100644
index 0000000000..3af67a2a04
--- /dev/null
+++ b/packages/swap/src/adapters/api/muesliswap/transformers.ts
@@ -0,0 +1,268 @@
+import {Portfolio, Swap} from '@yoroi/types'
+import {
+ CancelRequest,
+ CancelResponse,
+ ConstructSwapDatumRequest,
+ ConstructSwapDatumResponse,
+ LiquidityPoolRequest,
+ LiquidityPoolResponse,
+ OrdersAggregatorResponse,
+ OrdersHistoryResponse,
+ Pools,
+ Provider,
+ TokensResponse,
+} from './types'
+import {MuesliswapApiConfig} from './api-maker'
+import {asTokenFingerprint, asTokenName} from '../../../helpers/transformers'
+
+export const transformersMaker = ({
+ primaryTokenInfo,
+ address,
+ addressHex,
+}: MuesliswapApiConfig) => {
+ const asYoroiTokenId = ({
+ policyId,
+ name,
+ }: {
+ policyId: string
+ name: string
+ }): Portfolio.Token.Id => {
+ const possibleTokenId = `${policyId}.${name}`
+ // openswap is inconsistent about ADA
+ // sometimes is '.', '' or 'lovelace'
+
+ if (
+ policyId === '' ||
+ possibleTokenId === '.' ||
+ possibleTokenId === 'lovelace.'
+ )
+ return primaryTokenInfo.id
+ return `${policyId}.${name}`
+ }
+
+ return {
+ tokens: {
+ response: (res: TokensResponse): Array =>
+ res.map(({info}) => {
+ const id = asYoroiTokenId(info.address)
+
+ const isPrimary = id === primaryTokenInfo.id
+ if (isPrimary) return primaryTokenInfo
+ return {
+ id,
+ fingerprint: asTokenFingerprint({
+ policyId: info.address.policyId,
+ assetNameHex: info.address.name,
+ }),
+ name: asTokenName(info.address.name),
+ decimals: info.decimalPlaces,
+ description: info.description,
+ originalImage: info.image ?? '',
+ type: Portfolio.Token.Type.FT,
+ nature: Portfolio.Token.Nature.Secondary,
+ ticker: info.symbol,
+ symbol: info.sign ?? '',
+ status: Portfolio.Token.Status.Valid,
+ application: Portfolio.Token.Application.General,
+ reference: '',
+ tag: '',
+ website: info.website,
+ }
+ }),
+ },
+ ordersAggregator: {
+ response: (res: OrdersAggregatorResponse): Array =>
+ res.map(
+ ({
+ provider,
+ placedAt,
+ finalizedAt,
+ status,
+ fromToken: {address: fromToken},
+ toToken: {address: toToken},
+ fromAmount,
+ toAmount,
+ txHash,
+ outputIdx,
+ }) => ({
+ aggregator: Swap.Aggregator.Muesliswap,
+ dex: provider,
+ placedAt: placedAt ? placedAt * 1000 : undefined,
+ lastUpdate: finalizedAt ? finalizedAt * 1000 : undefined,
+ status,
+ tokenIn: asYoroiTokenId(fromToken),
+ tokenOut: asYoroiTokenId(toToken),
+ amountIn: Number(fromAmount),
+ actualAmountOut: 0,
+ expectedAmountOut: Number(toAmount),
+ txHash,
+ updateTxHash: txHash,
+ outputIndex: outputIdx ?? 0,
+ }),
+ ),
+ },
+ ordersHistory: {
+ response: (res: OrdersHistoryResponse): Array =>
+ res.map(
+ ({
+ fromToken: {address: fromToken},
+ toToken: {address: toToken},
+ placedAt,
+ finalizedAt,
+ receivedAmount,
+ toAmount,
+ fromAmount,
+ txHash,
+ status,
+ dex = 'muesliswap',
+ outputIdx,
+ }) => ({
+ aggregator: Swap.Aggregator.Muesliswap,
+ dex,
+ placedAt: placedAt ? placedAt * 1000 : undefined,
+ lastUpdate: finalizedAt ? finalizedAt * 1000 : undefined,
+ status,
+ tokenIn: asYoroiTokenId(fromToken),
+ tokenOut: asYoroiTokenId(toToken),
+ amountIn: Number(fromAmount),
+ actualAmountOut: Number(receivedAmount),
+ expectedAmountOut: Number(toAmount),
+ txHash,
+ updateTxHash: txHash,
+ outputIndex: outputIdx ?? 0,
+ }),
+ ),
+ },
+ cancel: {
+ request: ({order, collateral}: Swap.CancelRequest): CancelRequest => ({
+ wallet: addressHex,
+ utxo: `${order.txHash ?? ''}#${order.outputIndex}`,
+ collateralUtxo: collateral ?? '',
+ }),
+ response: ({cbor = ''}: CancelResponse): Swap.CancelResponse => ({
+ cbor,
+ }),
+ },
+ liquidityPools: {
+ request: ({
+ dex,
+ blacklistedDexes,
+ tokenIn,
+ tokenOut,
+ }: Swap.EstimateRequest): LiquidityPoolRequest => ({
+ 'only-verified': 'y',
+ 'providers': dex
+ ? dex
+ : Object.values(Provider)
+ .filter((provider) => !blacklistedDexes?.includes(provider))
+ .join(),
+ 'token-a': tokenIn,
+ 'token-b': tokenOut,
+ }),
+ response: (
+ pools: LiquidityPoolResponse,
+ {tokenIn, tokenOut}: Swap.EstimateRequest,
+ ): Pools =>
+ pools
+ .map(
+ ({
+ feeToken,
+ batcherFee,
+ poolFee,
+ lvlDeposit,
+ lpToken,
+ tokenA,
+ tokenB,
+ provider,
+ poolId,
+ }) => {
+ // Don't support pools with fees different than Ada yet
+ if (primaryTokenInfo.id !== asYoroiTokenId(feeToken.address))
+ return null
+
+ const A = {
+ price: tokenA.priceAda,
+ id: asYoroiTokenId(tokenA.address),
+ amount: Number(tokenA.amount),
+ decimals: tokenA.decimalPlaces,
+ }
+ const B = {
+ price: tokenB.priceAda,
+ id: asYoroiTokenId(tokenB.address),
+ amount: Number(tokenB.amount),
+ decimals: tokenB.decimalPlaces,
+ }
+ const [input, output] = tokenIn === A.id ? [A, B] : [B, A]
+
+ if (input.id !== tokenIn || input.id !== tokenOut) return null
+
+ return {
+ tokenIn: input.id,
+ tokenOut: output.id,
+ tokenInDecimals: input.decimals,
+ tokenOutDecimals: output.decimals,
+ tokenInSupply: Number(input.amount),
+ tokenOutSupply: Number(output.amount),
+ tokenInPtPrice: input.price,
+ tokenOutPtPrice: output.price,
+ deposit: Number(lvlDeposit),
+ lpTokenId: lpToken.address
+ ? asYoroiTokenId(lpToken.address)
+ : undefined,
+ batcherFee: Number(batcherFee),
+ fee: Number(poolFee),
+ poolId,
+ provider,
+ }
+ },
+ )
+ .filter((pool) => pool !== null),
+ },
+ constructSwapDatum: {
+ request: (
+ {tokenIn, tokenOut}: Swap.CreateRequest,
+ {dex, poolId, amountIn, expectedOutput}: Swap.Split,
+ ): ConstructSwapDatumRequest => {
+ const [sellTokenPolicyID, sellTokenNameHex] = tokenIn.split('.') as [
+ string,
+ string,
+ ]
+ const [buyTokenPolicyID, buyTokenNameHex] = tokenOut.split('.') as [
+ string,
+ string,
+ ]
+
+ return {
+ walletAddr: address,
+ protocol: dex as Provider,
+ poolId,
+ sellTokenPolicyID,
+ sellTokenNameHex,
+ sellAmount: amountIn.toString(),
+ buyTokenPolicyID,
+ buyTokenNameHex,
+ buyAmount: expectedOutput.toString(),
+ }
+ },
+ response: (
+ res: ConstructSwapDatumResponse,
+ estimate: Swap.EstimateResponse,
+ ): Swap.CreateResponse => {
+ const cbor: string = swapCreateCbor(res)
+
+ return {
+ cbor,
+ ...estimate,
+ totalInput: estimate.totalInput ?? estimate.splits[0]?.amountIn ?? 0,
+ }
+ },
+ },
+ } as const
+}
+
+// TODO: Transform contractAddress, datum, hash into cbor, code in StartSwapOrderScreen.tsx
+const swapCreateCbor = ({
+ address: contractAddress,
+ datum,
+ hash,
+}: ConstructSwapDatumResponse) => `${contractAddress}${datum}${hash}`
diff --git a/packages/swap/src/adapters/api/muesliswap/types.ts b/packages/swap/src/adapters/api/muesliswap/types.ts
new file mode 100644
index 0000000000..0684686045
--- /dev/null
+++ b/packages/swap/src/adapters/api/muesliswap/types.ts
@@ -0,0 +1,220 @@
+import {Portfolio} from '@yoroi/types'
+
+export type TokensResponse = Array<{
+ info: {
+ supply: {
+ total: string // total circulating supply of the token, without decimals.
+ circulating: string | null // if set the circulating supply of the token, if null the amount in circulation is unknown.
+ }
+ status: 'verified' | 'unverified' | 'scam' | 'outdated'
+ address: {
+ policyId: string // policy id of the token.
+ name: string // hexadecimal representation of token name.
+ }
+ symbol: string // shorthand token symbol.
+ image?: string // http link to the token image.
+ website: string
+ description: string
+ decimalPlaces: number // number of decimal places of the token, i.e. 6 for ADA and 0 for MILK.
+ categories: string[] // encoding categories as ids.
+ sign?: string // token sign, i.e. "₳" for ADA.
+ }
+ price: {
+ volume: {
+ base: string // float, trading volume 24h in base currency (e.g. ADA).
+ quote: string // float, trading volume 24h in quote currency.
+ }
+ volumeChange: {
+ base: number // float, percent change of trading volume in comparison to previous 24h.
+ quote: number // float, percent change of trading volume in comparison to previous 24h.
+ }
+ price: number // live trading price in base currency (e.g. ADA).
+ askPrice: number // lowest ask price in base currency (e.g. ADA).
+ bidPrice: number // highest bid price in base currency (e.g. ADA).
+ priceChange: {
+ '24h': string // float, price change last 24 hours.
+ '7d': string // float, price change last 7 days.
+ }
+ quoteDecimalPlaces: number // decimal places of quote token.
+ baseDecimalPlaces: number // decimal places of base token.
+ price10d: number[] //float, prices of this tokens averaged for the last 10 days, in chronological order i.e.oldest first.
+ }
+}>
+
+export type OrdersAggregatorResponse = Array<{
+ fromToken: {
+ address: {
+ policyId: string
+ name: string
+ }
+ symbol: string
+ image: string
+ decimalPlaces: number
+ }
+ toToken: {
+ address: {
+ policyId: string
+ name: string
+ }
+ symbol: string
+ image: string
+ decimalPlaces: number
+ }
+ batchToken: {
+ address: {
+ policyId: string
+ name: string
+ }
+ symbol: string
+ decimalPlaces: number
+ }
+ batcherFee: string
+ fromAmount: string
+ toAmount: string
+ attachedValues: [
+ {
+ address: {
+ policyId: string
+ name: string
+ }
+ amount: string
+ },
+ ]
+ owner: string
+ sender: string
+ providerSpecifics?: string
+ txHash: string
+ outputIdx: 0
+ status: 'open' | string
+ provider: string
+ placedAt?: number
+ finalizedAt?: number
+ batcherAddress: string
+}>
+
+export type OrdersHistoryResponse = Array<{
+ attachedLvl: number
+ finalizedAt: number
+ fromAmount: string
+ fromToken: TokensResponse[0]['info']
+ outputIdx: number | null
+ paidAmount: string
+ placedAt: number
+ pubKeyHash: string
+ receivedAmount: string | number
+ status: 'matched' | string
+ toAmount: string
+ toToken: TokensResponse[0]['info']
+ txHash: string
+ scriptVersion?: string
+ aggregatorPlatform?: string | null
+ stakeKeyHash?: string
+ dex?: string
+}>
+
+export type CancelRequest = {
+ utxo: string // order UTxO from the smart contract to cancel. e.g. "txhash#0".
+ collateralUtxo: string // collateral UTxOs to use for canceling the order in cbor format.
+ wallet: string // address of the wallet that owns the order in cbor format.
+}
+
+export type CancelResponse = {
+ status: 'success' | string
+ cbor: string
+}
+
+export const Provider = {
+ minswap: 'minswap',
+ sundaeswap: 'sundaeswap',
+ wingriders: 'wingriders',
+ muesliswap: 'muesliswap',
+ muesliswap_v1: 'muesliswap_v1',
+ muesliswap_v2: 'muesliswap_v2',
+ muesliswap_v3: 'muesliswap_v3',
+ muesliswap_v4: 'muesliswap_v4',
+ vyfi: 'vyfi',
+ spectrum: 'spectrum',
+} as const
+
+export type Provider = (typeof Provider)[keyof typeof Provider]
+
+export type LiquidityPoolRequest = {
+ 'only-verified': 'y' | 'n'
+ 'token-a': string
+ 'token-b': string
+ 'providers': string
+}
+
+export type PoolToken = {
+ address: {
+ policyId: string
+ name: string
+ }
+ symbol?: string
+ image?: string
+ decimalPlaces: number
+ amount: string
+ status: string
+ priceAda: number
+}
+export type LiquidityPoolResponse = Array<{
+ tokenA: PoolToken
+ tokenB: PoolToken
+ feeToken: Omit
+ batcherFee: string
+ lvlDeposit: string
+ poolFee: string
+ lpToken: {
+ address?: {
+ policyId: string
+ name: string
+ }
+ amount?: string
+ }
+ poolId: string
+ provider: Provider
+ txHash?: string
+ outputIdx?: number
+ volume24h?: number
+ volume7d?: number
+ liquidityApy?: number
+ priceASqrt?: any
+ priceBSqrt?: any
+ batcherAddress: string
+}>
+
+export type Pools = Array<{
+ tokenIn: Portfolio.Token.Id
+ tokenOut: Portfolio.Token.Id
+ tokenInDecimals: number
+ tokenOutDecimals: number
+ tokenInSupply: number
+ tokenOutSupply: number
+ tokenInPtPrice: number
+ tokenOutPtPrice: number
+ deposit: number
+ lpTokenId?: Portfolio.Token.Id
+ batcherFee: number
+ fee: number
+ poolId: string
+ provider: Provider
+}>
+
+export type ConstructSwapDatumRequest = {
+ walletAddr: string
+ protocol: Provider
+ poolId: string
+ sellTokenPolicyID: string
+ sellTokenNameHex: string
+ sellAmount: string
+ buyTokenPolicyID: string
+ buyTokenNameHex: string
+ buyAmount: string
+}
+
+export type ConstructSwapDatumResponse = {
+ status: 'success' | string
+ datum: string
+ hash: string
+ address: string
+}
diff --git a/packages/swap/src/adapters/openswap-api/api.mocks.ts b/packages/swap/src/adapters/openswap-api/api.mocks.ts
deleted file mode 100644
index 8c0e581a46..0000000000
--- a/packages/swap/src/adapters/openswap-api/api.mocks.ts
+++ /dev/null
@@ -1,322 +0,0 @@
-import {Portfolio, Swap} from '@yoroi/types'
-
-const getOpenOrders: Array = [
- {
- utxo: '1e977694e2413bd0e6105303bb44da60530cafe49b864dde8f8902b021ed86ba#0',
- provider: 'muesliswap_v4',
- from: {quantity: 1000000n, tokenId: '.'},
- to: {
- quantity: 41372n,
- tokenId:
- '2adf188218a66847024664f4f63939577627a56c090f679fe366c5ee.535441424c45',
- },
- deposit: {quantity: 1700000n, tokenId: '.'},
- owner:
- 'addr1qxxvt9rzpdxxysmqp50d7f5a3gdescgrejsu7zsdxqjy8yun4cngaq46gr8c9qyz4td9ddajzqhjnrqvfh0gspzv9xnsmq6nqx',
- },
-]
-
-const getCompletedOrders: Array = [
- {
- txHash: '0e56f8d48808e689c1aed60abc158b7aef21c3565a0b766dd89ffba31979414b',
- from: {quantity: 200n, tokenId: '.'},
- to: {
- quantity: 100n,
- tokenId:
- 'c04f4200502a998e9eebafac0291a1f38008de3fe146d136946d8f4b.415247454e54',
- },
- provider: 'minswap',
- placedAt: 1631635254000,
- },
-]
-
-const createOrderData: Swap.CreateOrderData = {
- address: 'someAddress',
- selectedPool: {
- provider: 'minswap',
- fee: '',
- tokenA: {
- tokenId:
- 'e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed72.43414b45',
- quantity: 1000n,
- },
- tokenB: {tokenId: '.', quantity: 1000000000n},
- ptPriceTokenA: '0',
- ptPriceTokenB: '0',
- batcherFee: {tokenId: '.', quantity: 0n},
- deposit: {tokenId: '.', quantity: 2000000n},
- poolId:
- '0be55d262b29f564998ff81efe21bdc0022621c12f15af08d0f2ddb1.7339a8bcda85e2c997d9f16beddbeb3ad755f5202f5cfd9cb08db346a1292c01',
- lpToken: {tokenId: '.', quantity: 0n},
- },
- amounts: {
- sell: {
- quantity: 1n,
- tokenId:
- 'e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed72.43414b45',
- },
- buy: {quantity: 100n, tokenId: '.'},
- },
- slippage: 1,
- limitPrice: undefined,
-}
-
-const getPools: Swap.Pool[] = [
- {
- tokenA: {quantity: 1233807687n, tokenId: '.'},
- tokenB: {
- quantity: 780n,
- tokenId:
- 'e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed72.43414b45',
- },
- ptPriceTokenA: '0',
- ptPriceTokenB: '0',
- deposit: {quantity: 2000000n, tokenId: '.'},
- lpToken: {
- quantity: 981004n,
- tokenId:
- 'e4214b7cce62ac6fbba385d164df48e157eae5863521b4b67ca71d86.7339a8bcda85e2c997d9f16beddbeb3ad755f5202f5cfd9cb08db346a1292c01',
- },
- batcherFee: {quantity: 2000000n, tokenId: '.'},
- fee: '0.3',
- poolId:
- '0be55d262b29f564998ff81efe21bdc0022621c12f15af08d0f2ddb1.7339a8bcda85e2c997d9f16beddbeb3ad755f5202f5cfd9cb08db346a1292c01',
- provider: 'minswap',
- },
- {
- tokenA: {quantity: 1233807687n, tokenId: '.'},
- tokenB: {
- quantity: 780n,
- tokenId:
- 'e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed72.43414b45',
- },
- ptPriceTokenA: '0',
- ptPriceTokenB: '0',
- deposit: {quantity: 2000000n, tokenId: '.'},
- lpToken: {
- quantity: 981004n,
- tokenId:
- 'e4214b7cce62ac6fbba385d164df48e157eae5863521b4b67ca71d86.7339a8bcda85e2c997d9f16beddbeb3ad755f5202f5cfd9cb08db346a1292c01',
- },
- batcherFee: {quantity: 2000000n, tokenId: '.'},
- fee: '0.3',
- poolId:
- '0be55d262b29f564998ff81efe21bdc0022621c12f15af08d0f2ddb1.7339a8bcda85e2c997d9f16beddbeb3ad755f5202f5cfd9cb08db346a1292c01',
- provider: 'sundaeswap',
- },
- {
- tokenA: {quantity: 1233807687n, tokenId: '.'},
- tokenB: {
- quantity: 780n,
- tokenId:
- 'e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed72.43414b45',
- },
- ptPriceTokenA: '0',
- ptPriceTokenB: '0',
- deposit: {quantity: 2000000n, tokenId: '.'},
- lpToken: {
- quantity: 981004n,
- tokenId:
- 'e4214b7cce62ac6fbba385d164df48e157eae5863521b4b67ca71d86.7339a8bcda85e2c997d9f16beddbeb3ad755f5202f5cfd9cb08db346a1292c01',
- },
- batcherFee: {quantity: 2000000n, tokenId: '.'},
- fee: '0.3',
- poolId:
- '0be55d262b29f564998ff81efe21bdc0022621c12f15af08d0f2ddb1.7339a8bcda85e2c997d9f16beddbeb3ad755f5202f5cfd9cb08db346a1292c01',
- provider: 'sundaeswap',
- },
-]
-
-const getTokenPairs: Array = [
- {
- application: Portfolio.Token.Application.General,
- nature: Portfolio.Token.Nature.Secondary,
- status: Portfolio.Token.Status.Valid,
- id: '1c1e38cfcc815d2015dbda6bee668b2e707ee3f9d038d96668fcf63c.4567677363617065436c75624561737465725a656e6e79',
- fingerprint: 'asset126v2sm79r8uxvk4ju64mr6srxrvm2x75fpg6w3',
- name: 'EggscapeClubEasterZenny',
- decimals: 0,
- description: 'Eggscape Club Utility Token',
- originalImage: 'ipfs://QmNYibJoiTWRiMmWn4yXwvoakEPgq9WmaukmRXHF1VGbAU',
- type: Portfolio.Token.Type.FT,
- symbol: '',
- ticker: 'EZY',
- tag: '',
- reference: '',
- website: 'https://eggscape.io/',
- },
- {
- application: Portfolio.Token.Application.General,
- nature: Portfolio.Token.Nature.Secondary,
- status: Portfolio.Token.Status.Valid,
- id: 'cdaaee586376139ee8c3cc4061623968810d177ca5c300afb890b48a.43415354',
- fingerprint: 'asset1yv4fx867hueqt98aqvjw5ncjymz8k3ah8zawcg',
- name: 'CAST',
- decimals: 0,
- description: 'Utility Token for Carda Station Metaverse',
- originalImage:
- 'https://tokens.muesliswap.com/static/img/tokens/cdaaee586376139ee8c3cc4061623968810d177ca5c300afb890b48a.43415354.png',
- type: Portfolio.Token.Type.FT,
- symbol: '',
- ticker: 'CAST',
- tag: '',
- reference: '',
- website: 'https://cardastation.com',
- },
- {
- application: Portfolio.Token.Application.General,
- nature: Portfolio.Token.Nature.Secondary,
- status: Portfolio.Token.Status.Valid,
- id: 'cd5b9dd91319edbb19477ad00cbef673a221e70a17ef043951fc6786.52656465656d61626c65',
- fingerprint: 'asset18qw75gcdldlu7q5xh8fjsemgvwffzkg8hatq3s',
- name: 'Redeemable',
- decimals: 4,
- description:
- 'The fiat-backed stablecoin issued by Shareslake. Powering the fully stable branch of Cardano.',
- originalImage:
- 'https://tokens.muesliswap.com/static/img/tokens/cd5b9dd91319edbb19477ad00cbef673a221e70a17ef043951fc6786.52656465656d61626c65.png',
- type: Portfolio.Token.Type.FT,
- symbol: '',
- ticker: 'RUSD',
- tag: '',
- reference: '',
- website: 'https://www.shareslake.com',
- },
- {
- application: Portfolio.Token.Application.General,
- nature: Portfolio.Token.Nature.Secondary,
- status: Portfolio.Token.Status.Valid,
- id: '2d420236ffaada336c21e3f4520b799f6e246d8618f2fc89a4907da6.4564756c6164646572546f6b656e',
- fingerprint: 'asset1ny2ehvl20cp5y7mmn5qq332sgdncdmsgrcqlwh',
- name: 'EduladderToken',
- decimals: 6,
- description: 'Proof Of Contribution.',
- originalImage:
- 'https://tokens.muesliswap.com/static/img/tokens/2d420236ffaada336c21e3f4520b799f6e246d8618f2fc89a4907da6.4564756c6164646572546f6b656e.png',
- type: Portfolio.Token.Type.FT,
- symbol: '',
- ticker: 'ELADR',
- tag: '',
- reference: '',
- website: 'https://eduladder.com',
- },
-]
-
-const getTokens: Array = [
- {
- application: Portfolio.Token.Application.General,
- decimals: 0,
- description: 'Eggscape Club Utility Token',
- fingerprint: 'asset126v2sm79r8uxvk4ju64mr6srxrvm2x75fpg6w3',
- id: '1c1e38cfcc815d2015dbda6bee668b2e707ee3f9d038d96668fcf63c.4567677363617065436c75624561737465725a656e6e79',
- name: 'EggscapeClubEasterZenny',
- nature: Portfolio.Token.Nature.Secondary,
- originalImage: 'ipfs://QmNYibJoiTWRiMmWn4yXwvoakEPgq9WmaukmRXHF1VGbAU',
- reference: '',
- status: Portfolio.Token.Status.Valid,
- symbol: '',
- tag: '',
- ticker: 'EZY',
- type: Portfolio.Token.Type.FT,
- website: 'https://eggscape.io/',
- },
- {
- application: Portfolio.Token.Application.General,
- decimals: 0,
- description: 'Utility Token for Carda Station Metaverse',
- fingerprint: 'asset1yv4fx867hueqt98aqvjw5ncjymz8k3ah8zawcg',
- id: 'cdaaee586376139ee8c3cc4061623968810d177ca5c300afb890b48a.43415354',
- name: 'CAST',
- nature: Portfolio.Token.Nature.Secondary,
- originalImage:
- 'https://tokens.muesliswap.com/static/img/tokens/cdaaee586376139ee8c3cc4061623968810d177ca5c300afb890b48a.43415354.png',
- reference: '',
- status: Portfolio.Token.Status.Valid,
- symbol: '',
- tag: '',
- ticker: 'CAST',
- type: Portfolio.Token.Type.FT,
- website: 'https://cardastation.com',
- },
- {
- application: Portfolio.Token.Application.General,
- decimals: 4,
- description:
- 'The fiat-backed stablecoin issued by Shareslake. Powering the fully stable branch of Cardano.',
- fingerprint: 'asset18qw75gcdldlu7q5xh8fjsemgvwffzkg8hatq3s',
- id: 'cd5b9dd91319edbb19477ad00cbef673a221e70a17ef043951fc6786.52656465656d61626c65',
- name: 'Redeemable',
- nature: Portfolio.Token.Nature.Secondary,
- originalImage:
- 'https://tokens.muesliswap.com/static/img/tokens/cd5b9dd91319edbb19477ad00cbef673a221e70a17ef043951fc6786.52656465656d61626c65.png',
- reference: '',
- status: Portfolio.Token.Status.Valid,
- symbol: '',
- tag: '',
- ticker: 'RUSD',
- type: Portfolio.Token.Type.FT,
- website: 'https://www.shareslake.com',
- },
- {
- application: Portfolio.Token.Application.General,
- decimals: 6,
- description: 'Proof Of Contribution.',
- fingerprint: 'asset1ny2ehvl20cp5y7mmn5qq332sgdncdmsgrcqlwh',
- id: '2d420236ffaada336c21e3f4520b799f6e246d8618f2fc89a4907da6.4564756c6164646572546f6b656e',
- name: 'EduladderToken',
- nature: Portfolio.Token.Nature.Secondary,
- originalImage:
- 'https://tokens.muesliswap.com/static/img/tokens/2d420236ffaada336c21e3f4520b799f6e246d8618f2fc89a4907da6.4564756c6164646572546f6b656e.png',
- reference: '',
- status: Portfolio.Token.Status.Valid,
- symbol: '',
- tag: '',
- ticker: 'ELADR',
- type: Portfolio.Token.Type.FT,
- website: 'https://eduladder.com',
- },
- {
- application: Portfolio.Token.Application.General,
- decimals: 6,
- description: 'Proof Of Contribution.',
- fingerprint: 'asset1ud7y8pzglxmf68jtww3xhpes9j87akx4mtyx28',
- id: '2d420236ffaada336c21e3f4520b799f6e246d8618f2fc89a4907da6.FFFFFF',
- name: 'FFFFFF',
- nature: Portfolio.Token.Nature.Secondary,
- originalImage:
- 'https://tokens.muesliswap.com/static/img/tokens/2d420236ffaada336c21e3f4520b799f6e246d8618f2fc89a4907da6.4564756c6164646572546f6b656e.png',
- reference: '',
- status: Portfolio.Token.Status.Valid,
- symbol: '',
- tag: '',
- ticker: 'ELADR',
- type: Portfolio.Token.Type.FT,
- website: 'https://eduladder.com',
- },
- {
- application: Portfolio.Token.Application.General,
- decimals: 6,
- description: 'Proof Of Contribution.',
- fingerprint: 'asset19caqweshdelqqf2u90n7xwxyv5wgsx69aakrce',
- id: '2d420236ffaada336c21e3f4520b799f6e246d8618f2fc89a4907da6.FFFFAA',
- name: 'FFFFAA',
- nature: Portfolio.Token.Nature.Secondary,
- originalImage: '',
- reference: '',
- status: Portfolio.Token.Status.Valid,
- symbol: '',
- tag: '',
- ticker: 'ELAD',
- type: Portfolio.Token.Type.FT,
- website: 'https://eduladder.com',
- },
-]
-
-export const apiMocks = {
- getOpenOrders,
- getCompletedOrders,
- createOrderData,
- getPools,
- getTokenPairs,
- getTokens,
-}
diff --git a/packages/swap/src/adapters/openswap-api/api.test.ts b/packages/swap/src/adapters/openswap-api/api.test.ts
deleted file mode 100644
index ebccbf8aff..0000000000
--- a/packages/swap/src/adapters/openswap-api/api.test.ts
+++ /dev/null
@@ -1,231 +0,0 @@
-import {OpenSwapApi} from './api'
-import {axiosClient} from './config'
-import {
- CancelOrderRequest,
- CreateOrderRequest,
- Network,
- Provider,
-} from './types'
-
-jest.mock('./config.ts')
-
-describe('OpenSwapApi constructor', () => {
- it('should throw an error for unsupported networks', () => {
- const unsupportedNetwork = 'testnet' // Assuming 'testnet' is not supported
- expect(() => new OpenSwapApi(unsupportedNetwork as Network)).toThrow(
- /Supported networks are/,
- )
- })
-
- it('should create an instance for supported networks', () => {
- const supportedNetwork = 'mainnet'
- const api = new OpenSwapApi(supportedNetwork)
- expect(api).toBeInstanceOf(OpenSwapApi)
- expect(api.network).toBe(supportedNetwork)
- })
-})
-
-describe('createOrder', () => {
- it('should call createOrder with correct parameters', async () => {
- const mockAxios = axiosClient as jest.Mocked
- mockAxios.get.mockImplementationOnce(() =>
- Promise.resolve({
- status: 200,
- data: 'test-createOrder',
- }),
- )
-
- const api = new OpenSwapApi('mainnet', axiosClient)
- const orderData: CreateOrderRequest = {
- walletAddress: 'walletAddress',
- protocol: 'sundaeswap',
- poolId: 'poolId',
- sell: {
- policyId: 'sell-policyId',
- assetName: 'buy-assetName',
- amount: '123',
- },
- buy: {
- policyId: 'buy-policyId',
- assetName: 'buy-assetName',
- amount: '321',
- },
- }
-
- const result = await api.createOrder(orderData)
-
- expect(result).toBe('test-createOrder')
- })
-})
-
-describe('cancelOrder', () => {
- it('should call cancelOrder with correct parameters', async () => {
- const mockAxios = axiosClient as jest.Mocked
- mockAxios.get.mockImplementationOnce(() =>
- Promise.resolve({
- status: 200,
- data: {cbor: 'test-cancelOrder'},
- }),
- )
-
- const api = new OpenSwapApi('mainnet', axiosClient)
- const orderData: CancelOrderRequest = {
- orderUTxO: 'orderUTxO',
- collateralUTxO: 'collateralUTxO',
- walletAddress: 'walletAddress',
- }
-
- const result = await api.cancelOrder(orderData)
-
- expect(result).toBe('test-cancelOrder')
- })
-})
-
-describe('getOrders', () => {
- it('should call getOrders with correct parameters', async () => {
- const mockAxios = axiosClient as jest.Mocked
- mockAxios.get.mockImplementationOnce(() =>
- Promise.resolve({
- status: 200,
- data: 'test-getOrders',
- }),
- )
-
- const api = new OpenSwapApi('mainnet', axiosClient)
- const stakeKeyHash = 'stake-key-hash'
-
- const result = await api.getOrders(stakeKeyHash)
-
- expect(result).toBe('test-getOrders')
- })
-})
-
-describe('getCompletedOrders', () => {
- it('should call getCompletedOrders with correct parameters', async () => {
- const mockAxios = axiosClient as jest.Mocked
- mockAxios.get.mockImplementationOnce(() =>
- Promise.resolve({
- status: 200,
- data: [{status: 'matched', test: 'test-getCompletedOrders'}],
- }),
- )
-
- const api = new OpenSwapApi('mainnet', axiosClient)
- const stakeKeyHash = 'stake-key-hash'
-
- const result = await api.getCompletedOrders(stakeKeyHash)
-
- expect(result).toEqual([
- {status: 'matched', test: 'test-getCompletedOrders'},
- ])
- })
-})
-
-describe('getPrice', () => {
- it('should call getPrice with correct parameters', async () => {
- const mockAxios = axiosClient as jest.Mocked
- mockAxios.get.mockImplementationOnce(() =>
- Promise.resolve({
- status: 200,
- data: 'test-getPrice',
- }),
- )
-
- const api = new OpenSwapApi('mainnet', axiosClient)
- const baseToken = {
- policyId: 'baseToken-policyId',
- name: 'baseToken-name',
- }
- const quoteToken = {
- policyId: 'quoteToken-policyId',
- name: 'quoteToken-name',
- }
-
- const result = await api.getPrice({baseToken, quoteToken})
-
- expect(result).toEqual('test-getPrice')
- })
-})
-
-describe('getPoolsPair', () => {
- it('should call getPoolsPair with correct parameters', async () => {
- const mockAxios = axiosClient as jest.Mocked
- mockAxios.get.mockImplementationOnce(() =>
- Promise.resolve({
- status: 200,
- data: 'test-getPoolsPair',
- }),
- )
-
- const api = new OpenSwapApi('mainnet', axiosClient)
- const tokenA = {
- policyId: 'tokenA-policyId',
- assetName: 'tokenA-name',
- }
- const tokenB = {
- policyId: 'tokenB-policyId',
- assetName: 'tokenB-name',
- }
-
- const result = await api.getPoolsPair({tokenA, tokenB})
-
- expect(result).toEqual('test-getPoolsPair')
- })
-})
-
-describe('getLiquidityPools', () => {
- it('should call getLiquidityPools with correct parameters', async () => {
- const mockAxios = axiosClient as jest.Mocked
- mockAxios.get.mockImplementationOnce(() =>
- Promise.resolve({
- status: 200,
- data: 'test-getLiquidityPools',
- }),
- )
-
- const api = new OpenSwapApi('mainnet', axiosClient)
- const tokenA = 'tokenA'
- const tokenB = 'tokenB'
- const providers: ReadonlyArray = ['spectrum']
-
- const result = await api.getLiquidityPools({tokenA, tokenB, providers})
-
- expect(result).toEqual('test-getLiquidityPools')
- })
-})
-
-describe('getTokenPairs', () => {
- it('should call getTokenPairs with correct parameters', async () => {
- const mockAxios = axiosClient as jest.Mocked
- mockAxios.get.mockImplementationOnce(() =>
- Promise.resolve({
- status: 200,
- data: 'test-getTokenPairs',
- }),
- )
-
- const api = new OpenSwapApi('mainnet', axiosClient)
-
- const result = await api.getTokenPairs()
-
- expect(result).toEqual('test-getTokenPairs')
- })
-})
-
-describe('getTokens', () => {
- it('should call getTokens with correct parameters', async () => {
- const mockAxios = axiosClient as jest.Mocked
- mockAxios.get.mockImplementationOnce(() =>
- Promise.resolve({
- status: 200,
- data: 'test-getTokens',
- }),
- )
-
- const api = new OpenSwapApi('mainnet', axiosClient)
-
- const result = await api.getTokens()
-
- expect(result).toEqual('test-getTokens')
- })
-})
diff --git a/packages/swap/src/adapters/openswap-api/api.ts b/packages/swap/src/adapters/openswap-api/api.ts
deleted file mode 100644
index 55351829dc..0000000000
--- a/packages/swap/src/adapters/openswap-api/api.ts
+++ /dev/null
@@ -1,116 +0,0 @@
-import {AxiosInstance} from 'axios'
-import {
- cancelOrder, // returns an unsigned transaction to cancel the order.
- createOrder, // returns a datum and a contract address to create the order transaction.
- getCompletedOrders,
- getOrders, // returns all orders for a given stake key hash.
-} from './orders'
-import {getTokenPairs} from './token-pairs'
-import {getTokens} from './tokens'
-import {
- CancelOrderRequest,
- CreateOrderRequest,
- Network,
- Provider,
- PriceAddress,
- TokenAddress,
-} from './types'
-import {axiosClient} from './config'
-import {getPrice} from './price'
-import {getLiquidityPools, getPoolsPair} from './pools'
-
-export class OpenSwapApi {
- constructor(
- public readonly network: Network,
- private readonly client: AxiosInstance = axiosClient,
- ) {
- if (!supportedNetworks.includes(network)) {
- throw new Error(
- `Supported networks are ${supportedNetworks.join(
- ', ',
- )}, got ${network}`,
- )
- }
- }
-
- public async createOrder(orderData: CreateOrderRequest) {
- return createOrder({network: this.network, client: this.client}, orderData)
- }
-
- public async cancelOrder(orderData: CancelOrderRequest) {
- return cancelOrder({network: this.network, client: this.client}, orderData)
- }
-
- public async getOrders(stakeKeyHash: string) {
- return getOrders(
- {network: this.network, client: this.client},
- {stakeKeyHash},
- )
- }
-
- public async getCompletedOrders(stakeKeyHash: string) {
- return getCompletedOrders(
- {network: this.network, client: this.client},
- {stakeKeyHash},
- )
- }
-
- public async getPrice({
- baseToken,
- quoteToken,
- }: {
- baseToken: PriceAddress
- quoteToken: PriceAddress
- }) {
- return getPrice(
- {network: this.network, client: this.client},
- {baseToken, quoteToken},
- )
- }
-
- public async getPoolsPair({
- tokenA,
- tokenB,
- }: {
- tokenA: TokenAddress
- tokenB: TokenAddress
- }) {
- return getPoolsPair(
- {network: this.network, client: this.client},
- {tokenA, tokenB},
- )
- }
-
- public async getLiquidityPools({
- tokenA,
- tokenB,
- providers,
- }: {
- tokenA: string
- tokenB: string
- providers: ReadonlyArray
- }) {
- return getLiquidityPools(
- {network: this.network, client: this.client},
- {tokenA, tokenB, providers},
- )
- }
-
- public async getTokenPairs({policyId = '', assetName = ''} = {}) {
- const tokenPairs = await getTokenPairs(
- {network: this.network, client: this.client},
- {policyId, assetName},
- )
-
- return tokenPairs
- }
-
- public async getTokens() {
- return getTokens({network: this.network, client: this.client})
- }
-}
-
-export const supportedNetworks: ReadonlyArray = [
- 'mainnet',
- 'preprod',
-] as const
diff --git a/packages/swap/src/adapters/openswap-api/config.ts b/packages/swap/src/adapters/openswap-api/config.ts
deleted file mode 100644
index 4110dd262a..0000000000
--- a/packages/swap/src/adapters/openswap-api/config.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import axios from 'axios'
-
-export const SWAP_API_ENDPOINTS = {
- mainnet: {
- getPrice: 'https://api.muesliswap.com/price',
- getPoolsPair: 'https://onchain2.muesliswap.com/pools/pair',
- getLiquidityPools: 'https://api.muesliswap.com/liquidity/pools',
- getOrders: 'https://onchain2.muesliswap.com/orders/all/',
- getCompletedOrders: 'https://api.muesliswap.com/orders/v3/history',
- getTokenPairs: 'https://api.muesliswap.com/list',
- getTokens: 'https://api.muesliswap.com/token-list',
- constructSwapDatum: 'https://aggregator.muesliswap.com/constructSwapDatum',
- cancelSwapTransaction:
- 'https://aggregator.muesliswap.com/cancelSwapTransaction',
- },
- preprod: {
- getPrice: 'https://preprod.api.muesliswap.com/price',
- getPoolsPair: 'https://preprod.pools.muesliswap.com/pools/pair',
- getLiquidityPools: 'https://preprod.api.muesliswap.com/liquidity/pools',
- getOrders: 'https://preprod.pools.muesliswap.com/orders/all/',
- getCompletedOrders: 'https://preprod.api.muesliswap.com/orders/v3/history',
- getTokenPairs: 'https://preprod.api.muesliswap.com/list',
- getTokens: 'https://preprod.api.muesliswap.com/token-list',
- constructSwapDatum:
- 'https://aggregator.muesliswap.com/constructTestnetSwapDatum',
- cancelSwapTransaction:
- 'https://aggregator.muesliswap.com/cancelTestnetSwapTransaction',
- },
-} as const
-
-export const axiosClient = axios.create({
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json',
- },
-})
diff --git a/packages/swap/src/adapters/openswap-api/openswap.mocks.ts b/packages/swap/src/adapters/openswap-api/openswap.mocks.ts
deleted file mode 100644
index a126706038..0000000000
--- a/packages/swap/src/adapters/openswap-api/openswap.mocks.ts
+++ /dev/null
@@ -1,697 +0,0 @@
-import {
- CompletedOrder,
- LiquidityPool,
- ListTokensResponse,
- OpenOrder,
- PriceResponse,
- TokenPairsResponse,
-} from './types'
-
-const getTokens: ListTokensResponse = [
- {
- supply: {
- total: '10000000',
- circulating: '300',
- },
- status: 'verified',
- website: 'https://eggscape.io/',
- image: 'ipfs://QmNYibJoiTWRiMmWn4yXwvoakEPgq9WmaukmRXHF1VGbAU',
- description: 'Eggscape Club Utility Token',
- address: {
- policyId: '1c1e38cfcc815d2015dbda6bee668b2e707ee3f9d038d96668fcf63c',
- name: '4567677363617065436c75624561737465725a656e6e79',
- },
- symbol: 'EZY',
- decimalPlaces: 0,
- categories: [],
- },
- {
- supply: {
- total: '1500000000',
- circulating: null,
- },
- status: 'verified',
- symbol: 'CAST',
- decimalPlaces: 0,
- image:
- 'https://tokens.muesliswap.com/static/img/tokens/cdaaee586376139ee8c3cc4061623968810d177ca5c300afb890b48a.43415354.png',
- description: 'Utility Token for Carda Station Metaverse',
- address: {
- policyId: 'cdaaee586376139ee8c3cc4061623968810d177ca5c300afb890b48a',
- name: '43415354',
- },
- website: 'https://cardastation.com',
- categories: [],
- },
- {
- supply: {
- total: '387017195',
- circulating: null,
- },
- status: 'verified',
- website: 'https://www.shareslake.com',
- description:
- 'The fiat-backed stablecoin issued by Shareslake. Powering the fully stable branch of Cardano.',
- image:
- 'https://tokens.muesliswap.com/static/img/tokens/cd5b9dd91319edbb19477ad00cbef673a221e70a17ef043951fc6786.52656465656d61626c65.png',
- symbol: 'RUSD',
- decimalPlaces: 4,
- address: {
- policyId: 'cd5b9dd91319edbb19477ad00cbef673a221e70a17ef043951fc6786',
- name: '52656465656d61626c65',
- },
- categories: [],
- },
- {
- supply: {
- total: '45000000003000000',
- circulating: null,
- },
- status: 'verified',
- website: 'https://eduladder.com',
- symbol: 'ELADR',
- decimalPlaces: 6,
- image:
- 'https://tokens.muesliswap.com/static/img/tokens/2d420236ffaada336c21e3f4520b799f6e246d8618f2fc89a4907da6.4564756c6164646572546f6b656e.png',
- description: 'Proof Of Contribution.',
- address: {
- policyId: '2d420236ffaada336c21e3f4520b799f6e246d8618f2fc89a4907da6',
- name: '4564756c6164646572546f6b656e',
- },
- categories: [],
- },
- {
- supply: {
- total: '45000000003000000',
- circulating: null,
- },
- status: 'verified',
- website: 'https://eduladder.com',
- symbol: 'ELADR',
- decimalPlaces: 6,
- image:
- 'https://tokens.muesliswap.com/static/img/tokens/2d420236ffaada336c21e3f4520b799f6e246d8618f2fc89a4907da6.4564756c6164646572546f6b656e.png',
- description: 'Proof Of Contribution.',
- address: {
- policyId: '2d420236ffaada336c21e3f4520b799f6e246d8618f2fc89a4907da6',
- name: 'FFFFFF',
- },
- categories: [],
- },
- {
- supply: {
- total: '45000000003000000',
- circulating: null,
- },
- status: 'verified',
- website: 'https://eduladder.com',
- symbol: 'ELAD',
- decimalPlaces: 6,
- description: 'Proof Of Contribution.',
- address: {
- policyId: '2d420236ffaada336c21e3f4520b799f6e246d8618f2fc89a4907da6',
- name: 'FFFFAA',
- },
- categories: [],
- },
-]
-
-const getTokenPairs: TokenPairsResponse = [
- {
- info: {
- supply: {
- total: '10000000',
- circulating: '300',
- },
- status: 'verified',
- website: 'https://eggscape.io/',
- image: 'ipfs://QmNYibJoiTWRiMmWn4yXwvoakEPgq9WmaukmRXHF1VGbAU',
- description: 'Eggscape Club Utility Token',
- address: {
- policyId: '1c1e38cfcc815d2015dbda6bee668b2e707ee3f9d038d96668fcf63c',
- name: '4567677363617065436c75624561737465725a656e6e79',
- },
- symbol: 'EZY',
- decimalPlaces: 0,
- categories: [],
- },
- price: {
- volume: {
- base: '0',
- quote: '0',
- },
- volumeChange: {
- base: 0,
- quote: 0,
- },
- // volumeTotal: {
- // base: 0,
- // quote: 0,
- // },
- // volumeAggregator: {},
- price: 5052.63204588242,
- askPrice: 9997.99630605055,
- bidPrice: 107.26778571429,
- priceChange: {
- '24h': '0.0',
- '7d': '0.0',
- },
- // fromToken: '.',
- // toToken:
- // '1c1e38cfcc815d2015dbda6bee668b2e707ee3f9d038d96668fcf63c.4567677363617065436c75624561737465725a656e6e79',
- price10d: [
- 10004.374362563743, 10004.374362563743, 10004.374362563743,
- 10004.374362563743, 10004.374362563743, 10004.374362563743,
- 10004.374362563743, 10004.374362563743, 10004.374362563743,
- 10004.374362563743,
- ],
- quoteDecimalPlaces: 0,
- baseDecimalPlaces: 6,
- // quoteAddress: {
- // policyId: '1c1e38cfcc815d2015dbda6bee668b2e707ee3f9d038d96668fcf63c',
- // name: '4567677363617065436c75624561737465725a656e6e79',
- // },
- // baseAddress: {
- // policyId: '',
- // name: '',
- // },
- },
- },
- {
- info: {
- supply: {
- total: '1500000000',
- circulating: null,
- },
- status: 'verified',
- symbol: 'CAST',
- decimalPlaces: 0,
- image:
- 'https://tokens.muesliswap.com/static/img/tokens/cdaaee586376139ee8c3cc4061623968810d177ca5c300afb890b48a.43415354.png',
- description: 'Utility Token for Carda Station Metaverse',
- address: {
- policyId: 'cdaaee586376139ee8c3cc4061623968810d177ca5c300afb890b48a',
- name: '43415354',
- },
- website: 'https://cardastation.com',
- categories: [],
- },
- price: {
- volume: {
- base: '0',
- quote: '0',
- },
- volumeChange: {
- base: 0,
- quote: 0,
- },
- // volumeTotal: {
- // base: 0,
- // quote: 0,
- // },
- // volumeAggregator: {},
- price: 402.13135196041,
- askPrice: 1000,
- bidPrice: 200.33388981636,
- priceChange: {
- '24h': '0.0',
- '7d': '0.0',
- },
- // fromToken: '.',
- // toToken:
- // 'cdaaee586376139ee8c3cc4061623968810d177ca5c300afb890b48a.43415354',
- price10d: [
- 690.7737494922812, 690.7737494922812, 690.7737494922812,
- 690.7737494922812, 690.7737494922812, 690.7737494922812,
- 690.7737494922812, 690.7737494922812, 690.7737494922812,
- 690.7737494922812,
- ],
- quoteDecimalPlaces: 0,
- baseDecimalPlaces: 6,
- // quoteAddress: {
- // policyId: 'cdaaee586376139ee8c3cc4061623968810d177ca5c300afb890b48a',
- // name: '43415354',
- // },
- // baseAddress: {
- // policyId: '',
- // name: '',
- // },
- },
- },
- {
- info: {
- supply: {
- total: '387017195',
- circulating: null,
- },
- status: 'verified',
- website: 'https://www.shareslake.com',
- description:
- 'The fiat-backed stablecoin issued by Shareslake. Powering the fully stable branch of Cardano.',
- image:
- 'https://tokens.muesliswap.com/static/img/tokens/cd5b9dd91319edbb19477ad00cbef673a221e70a17ef043951fc6786.52656465656d61626c65.png',
- symbol: 'RUSD',
- decimalPlaces: 4,
- address: {
- policyId: 'cd5b9dd91319edbb19477ad00cbef673a221e70a17ef043951fc6786',
- name: '52656465656d61626c65',
- },
- categories: [],
- },
- price: {
- volume: {
- base: '0',
- quote: '0',
- },
- volumeChange: {
- base: 0,
- quote: 0,
- },
- // volumeTotal: {
- // base: 0,
- // quote: 0,
- // },
- // volumeAggregator: {},
- price: 222.76258782201,
- askPrice: 240.60714285714,
- bidPrice: 204.91803278689,
- priceChange: {
- '24h': '0',
- '7d': '0',
- },
- // fromToken: '.',
- // toToken:
- // 'cd5b9dd91319edbb19477ad00cbef673a221e70a17ef043951fc6786.52656465656d61626c65',
- price10d: [],
- quoteDecimalPlaces: 4,
- baseDecimalPlaces: 6,
- // quoteAddress: {
- // policyId: 'cd5b9dd91319edbb19477ad00cbef673a221e70a17ef043951fc6786',
- // name: '52656465656d61626c65',
- // },
- // baseAddress: {
- // policyId: '',
- // name: '',
- // },
- },
- },
- {
- info: {
- supply: {
- total: '45000000003000000',
- circulating: null,
- },
- status: 'verified',
- website: 'https://eduladder.com',
- symbol: 'ELADR',
- decimalPlaces: 6,
- image:
- 'https://tokens.muesliswap.com/static/img/tokens/2d420236ffaada336c21e3f4520b799f6e246d8618f2fc89a4907da6.4564756c6164646572546f6b656e.png',
- description: 'Proof Of Contribution.',
- address: {
- policyId: '2d420236ffaada336c21e3f4520b799f6e246d8618f2fc89a4907da6',
- name: '4564756c6164646572546f6b656e',
- },
- categories: [],
- },
- price: {
- volume: {
- base: '0',
- quote: '0',
- },
- volumeChange: {
- base: 0,
- quote: 0,
- },
- // volumeTotal: {
- // base: 0,
- // quote: 0,
- // },
- // volumeAggregator: {},
- price: 1.94e-8,
- askPrice: 1.995e-8,
- bidPrice: 1.885e-8,
- priceChange: {
- '24h': '0.0',
- '7d': '0.0',
- },
- // fromToken: '.',
- // toToken:
- // '2d420236ffaada336c21e3f4520b799f6e246d8618f2fc89a4907da6.4564756c6164646572546f6b656e',
- price10d: [
- 1.4529723607353359e-8, 1.4529723607353359e-8, 1.4529723607353359e-8,
- 1.4529723607353359e-8, 1.4529723607353359e-8, 1.4529723607353359e-8,
- 1.4529723607353359e-8, 1.4529723607353359e-8, 1.4529723607353359e-8,
- 1.4529723607353359e-8,
- ],
- quoteDecimalPlaces: 6,
- baseDecimalPlaces: 6,
- // quoteAddress: {
- // policyId: '2d420236ffaada336c21e3f4520b799f6e246d8618f2fc89a4907da6',
- // name: '4564756c6164646572546f6b656e',
- // },
- // baseAddress: {
- // policyId: '',
- // name: '',
- // },
- },
- },
-]
-
-const getCompletedOrders: CompletedOrder[] = [
- {
- toToken: {
- address: {
- policyId: 'c04f4200502a998e9eebafac0291a1f38008de3fe146d136946d8f4b',
- name: '415247454e54',
- },
- },
- toAmount: '100',
- fromToken: {
- address: {
- policyId: '',
- name: '',
- },
- },
- fromAmount: '200',
- placedAt: 1631635254, // Unix timestamp
- status: 'completed',
- receivedAmount: '100',
- paidAmount: '200',
- finalizedAt: 1631635354, // You can specify a more specific type if needed
- txHash: '0e56f8d48808e689c1aed60abc158b7aef21c3565a0b766dd89ffba31979414b',
- outputIdx: 0,
- attachedLvl: 'someAttachedLvl',
- scriptVersion: 'v1',
- pubKeyHash: 'somePubKeyHash',
- dex: 'minswap',
- },
-]
-
-const getOpenOrders: OpenOrder[] = [
- {
- from: {
- amount: '1000000',
- token: '.',
- },
- to: {
- amount: '41372',
- token:
- '2adf188218a66847024664f4f63939577627a56c090f679fe366c5ee.535441424c45',
- },
- // sender:
- // 'addr1qy0556dz9jssrrnhv0g3ga98uczdd465cut9jjs5a4k5qy3yl52kwxsh5wfx3darrc4xwql43ylj2n29dpq3xg46a6mska8vfz',
- // owner:
- // 'addr1qy0556dz9jssrrnhv0g3ga98uczdd465cut9jjs5a4k5qy3yl52kwxsh5wfx3darrc4xwql43ylj2n29dpq3xg46a6mska8vfz',
- // ownerPubKeyHash: '1f4a69a22ca1018e7763d11474a7e604d6d754c716594a14ed6d4012',
- // ownerStakeKeyHash:
- // '24fd15671a17a39268b7a31e2a6703f5893f254d4568411322baeeb7',
- // batcherFee: {
- // amount: '950000',
- // token: '.',
- // },
- deposit: '1700000',
- // valueAttached: [
- // {
- // amount: '3650000',
- // token: '.',
- // },
- // ],
- utxo: '1e977694e2413bd0e6105303bb44da60530cafe49b864dde8f8902b021ed86ba#0',
- provider: 'muesliswap_v4',
- // allowPartial: true,
- owner:
- 'addr1qxxvt9rzpdxxysmqp50d7f5a3gdescgrejsu7zsdxqjy8yun4cngaq46gr8c9qyz4td9ddajzqhjnrqvfh0gspzv9xnsmq6nqx',
- },
-]
-
-const getLiquidityPools: LiquidityPool[] = [
- {
- provider: 'minswap',
- poolFee: '0.3',
- tokenA: {
- amount: '1233807687',
- address: {
- policyId: '',
- name: '',
- },
- symbol: '',
- image: '',
- decimalPlaces: 0,
- status: '',
- priceAda: 0,
- },
- tokenB: {
- amount: '780',
- address: {
- policyId: 'e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed72',
- name: '43414b45',
- },
- symbol: '',
- image: '',
- decimalPlaces: 0,
- status: '',
- priceAda: 0,
- },
- batcherFee: '2000000',
- // depositFee: {
- // amount: '2000000',
- // token: '.',
- // },
- lvlDeposit: '2000000',
- batcherAddress: 'someBatcherAddress',
- feeToken: {
- address: {
- policyId: '.',
- name: '.',
- },
- decimalPlaces: 0,
- },
- txHash: '0596860b5970ef989c56f7ae38b3c0f74bb4979ac15ee994c30760f7f4d908ce',
- outputIdx: 0,
- poolId:
- '0be55d262b29f564998ff81efe21bdc0022621c12f15af08d0f2ddb1.7339a8bcda85e2c997d9f16beddbeb3ad755f5202f5cfd9cb08db346a1292c01',
- lpToken: {
- amount: '981004',
- address: {
- policyId: 'e4214b7cce62ac6fbba385d164df48e157eae5863521b4b67ca71d86',
- name: '7339a8bcda85e2c997d9f16beddbeb3ad755f5202f5cfd9cb08db346a1292c01',
- },
- },
- },
- {
- provider: 'sundaeswap',
- poolFee: '0.3',
- tokenA: {
- amount: '1233807687',
- address: {
- policyId: '',
- name: '',
- },
- symbol: '',
- image: '',
- decimalPlaces: 0,
- status: '',
- priceAda: 0,
- },
- tokenB: {
- amount: '780',
- address: {
- policyId: 'e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed72',
- name: '43414b45',
- },
- symbol: '',
- image: '',
- decimalPlaces: 0,
- status: '',
- priceAda: 0,
- },
- batcherFee: '2000000',
- // depositFee: {
- // amount: '2000000',
- // token: '.',
- // },
- lvlDeposit: '2000000',
- txHash: '0596860b5970ef989c56f7ae38b3c0f74bb4979ac15ee994c30760f7f4d908ce',
- outputIdx: 0,
- batcherAddress: 'someBatcherAddress',
- feeToken: {
- address: {
- policyId: '.',
- name: '.',
- },
- decimalPlaces: 0,
- },
- poolId:
- '0be55d262b29f564998ff81efe21bdc0022621c12f15af08d0f2ddb1.7339a8bcda85e2c997d9f16beddbeb3ad755f5202f5cfd9cb08db346a1292c01',
- lpToken: {
- amount: '981004',
- address: {
- policyId: 'e4214b7cce62ac6fbba385d164df48e157eae5863521b4b67ca71d86',
- name: '7339a8bcda85e2c997d9f16beddbeb3ad755f5202f5cfd9cb08db346a1292c01',
- },
- },
- },
- {
- provider: 'sundaeswap',
- poolFee: '0.3',
- tokenA: {
- amount: '1233807687',
- address: {
- policyId: '',
- name: '',
- },
- symbol: '',
- image: '',
- decimalPlaces: 0,
- status: '',
- priceAda: 0,
- },
- tokenB: {
- amount: '780',
- address: {
- policyId: 'e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed72',
- name: '43414b45',
- },
- symbol: '',
- image: '',
- decimalPlaces: 0,
- status: '',
- priceAda: 0,
- },
- batcherFee: '2000000',
- // depositFee: {
- // amount: '2000000',
- // token: '.',
- // },
- lvlDeposit: '2000000',
- txHash: '0596860b5970ef989c56f7ae38b3c0f74bb4979ac15ee994c30760f7f4d908ce',
- outputIdx: 0,
- batcherAddress: 'someBatcherAddress',
- feeToken: {
- address: {
- policyId: '.',
- name: '.',
- },
- decimalPlaces: 0,
- },
- poolId:
- '0be55d262b29f564998ff81efe21bdc0022621c12f15af08d0f2ddb1.7339a8bcda85e2c997d9f16beddbeb3ad755f5202f5cfd9cb08db346a1292c01',
- lpToken: {
- amount: '981004',
- address: {
- policyId: 'e4214b7cce62ac6fbba385d164df48e157eae5863521b4b67ca71d86',
- name: '7339a8bcda85e2c997d9f16beddbeb3ad755f5202f5cfd9cb08db346a1292c01',
- },
- },
- },
- {
- provider: 'spectrum', // unsupported pool
- poolFee: '0.3',
- tokenA: {
- amount: '1233807687',
- address: {
- policyId: '',
- name: '',
- },
- symbol: '',
- image: '',
- decimalPlaces: 0,
- status: '',
- priceAda: 0,
- },
- tokenB: {
- amount: '780',
- address: {
- policyId: 'e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed72',
- name: '43414b45',
- },
- symbol: '',
- image: '',
- decimalPlaces: 0,
- status: '',
- priceAda: 0,
- },
- batcherFee: '2000000',
- // depositFee: {
- // amount: '2000000',
- // token: '.',
- // },
- lvlDeposit: '2000000',
- txHash: '0596860b5970ef989c56f7ae38b3c0f74bb4979ac15ee994c30760f7f4d908ce',
- outputIdx: 0,
- batcherAddress: 'someBatcherAddress',
- feeToken: {
- address: {
- policyId: '.',
- name: '.',
- },
- decimalPlaces: 0,
- },
- poolId:
- '0be55d262b29f564998ff81efe21bdc0022621c12f15af08d0f2ddb1.7339a8bcda85e2c997d9f16beddbeb3ad755f5202f5cfd9cb08db346a1292c01',
- lpToken: {
- amount: '981004',
- address: {
- policyId: 'e4214b7cce62ac6fbba385d164df48e157eae5863521b4b67ca71d86',
- name: '7339a8bcda85e2c997d9f16beddbeb3ad755f5202f5cfd9cb08db346a1292c01',
- },
- },
- },
-]
-
-const getPrice: PriceResponse = {
- baseDecimalPlaces: 6,
- quoteDecimalPlaces: 6,
- baseAddress: {
- policyId: '',
- name: '',
- },
- quoteAddress: {
- policyId: '29d222ce763455e3d7a09a665ce554f00ac89d2e99a1a83d267170c6',
- name: '4d494e',
- },
- askPrice: 0.08209814208,
- bidPrice: 0.06319999985,
- price: 0.07080044463,
- volume: {
- base: '14735349',
- quote: '211287611',
- },
- volumeAggregator: {
- minswap: {
- quote: 107413106646,
- base: 7651672996,
- },
- sundaeswap: {
- quote: 566084169,
- base: 39000000,
- },
- vyfi: {
- quote: 12370434748,
- base: 879028993,
- },
- },
- volumeTotal: {
- base: 8584437338,
- quote: 120560913174,
- },
- volumeChange: {
- base: 0,
- quote: 0,
- },
- priceChange: {
- '24h': '-0.2374956426253183',
- '7d': '8.757469657697857',
- },
- marketCap: 68873484244745.086,
-}
-
-export const openswapMocks = {
- getTokenPairs,
- getTokens,
- getPrice,
- getCompletedOrders,
- getOpenOrders,
- getLiquidityPools,
-}
diff --git a/packages/swap/src/adapters/openswap-api/orders.test.ts b/packages/swap/src/adapters/openswap-api/orders.test.ts
deleted file mode 100644
index 63b03c4cfc..0000000000
--- a/packages/swap/src/adapters/openswap-api/orders.test.ts
+++ /dev/null
@@ -1,274 +0,0 @@
-import {createOrder, cancelOrder, getOrders, getCompletedOrders} from './orders'
-import axios from 'axios'
-import {axiosClient} from './config'
-
-jest.mock('./config')
-
-const ADA_TOKEN = {
- policyId: '',
- assetName: '',
-}
-
-const GENS_TOKEN = {
- policyId: 'dda5fdb1002f7389b33e036b6afee82a8189becb6cba852e8b79b4fb',
- assetName: '0014df1047454e53',
-}
-
-describe('SwapOrdersApi', () => {
- describe('getOrders', () => {
- it('Should return orders list using staking key hash', async () => {
- const mockAxios = axiosClient as jest.Mocked
- mockAxios.get.mockImplementationOnce(() =>
- Promise.resolve({
- data: mockedOrders,
- status: 200,
- }),
- )
- const result = await getOrders(
- {network: 'preprod', client: mockAxios},
- {
- stakeKeyHash:
- '24fd15671a17a39268b7a31e2a6703f5893f254d4568411322baeeb7',
- },
- )
- expect(result).toHaveLength(1)
- })
-
- it('Should throws an error', async () => {
- const mockAxios = axiosClient as jest.Mocked
- mockAxios.get.mockImplementationOnce(() =>
- Promise.resolve({
- data: 'fake-error',
- status: 400,
- }),
- )
- await expect(() =>
- getOrders(
- {network: 'preprod', client: mockAxios},
- {
- stakeKeyHash:
- '24fd15671a17a39268b7a31e2a6703f5893f254d4568411322baeeb7',
- },
- ),
- ).rejects.toThrowError(
- /^Failed to get orders for 24fd15671a17a39268b7a31e2a6703f5893f254d4568411322baeeb7$/,
- )
- })
- })
-
- describe('getCompletedOrders', () => {
- it('Should return orders list using staking key hash', async () => {
- const mockAxios = axiosClient as jest.Mocked
- mockAxios.get.mockImplementationOnce(() =>
- Promise.resolve({
- data: mockedCompleteOrders,
- status: 200,
- }),
- )
- const result = await getCompletedOrders(
- {network: 'preprod', client: mockAxios},
- {
- stakeKeyHash:
- '24fd15671a17a39268b7a31e2a6703f5893f254d4568411322baeeb7',
- },
- )
- expect(result).toHaveLength(1)
- })
-
- it('Should throws an error', async () => {
- const mockAxios = axiosClient as jest.Mocked
- mockAxios.get.mockImplementationOnce(() =>
- Promise.resolve({
- data: 'fake-error',
- status: 400,
- }),
- )
- await expect(() =>
- getCompletedOrders(
- {network: 'preprod', client: mockAxios},
- {
- stakeKeyHash:
- '24fd15671a17a39268b7a31e2a6703f5893f254d4568411322baeeb7',
- },
- ),
- ).rejects.toThrowError(
- /^Failed to get orders for 24fd15671a17a39268b7a31e2a6703f5893f254d4568411322baeeb7$/,
- )
- })
- })
-
- describe('createOrder', () => {
- it('should create order and return datum, datumHash, and contract address', async () => {
- const mockAxios = axiosClient as jest.Mocked
- mockAxios.get.mockImplementationOnce(() =>
- Promise.resolve({
- status: 200,
- data: mockedCreateOrderResult,
- }),
- )
-
- const order = await createOrder(
- {network: 'mainnet', client: mockAxios},
- createOrderParams,
- )
-
- expect(order).toEqual(mockedCreateOrderResult)
- })
-
- it('should throw error for invalid order: custom message', async () => {
- const mockAxios = axiosClient as jest.Mocked
- mockAxios.get.mockImplementationOnce(() =>
- Promise.resolve({
- status: 200,
- data: {status: 'failed', reason: 'error_message'},
- }),
- )
- await expect(() =>
- createOrder({network: 'preprod', client: mockAxios}, createOrderParams),
- ).rejects.toThrowError(/^error_message$/)
- })
-
- it('should throw error for invalid order: default message', async () => {
- const mockAxios = axiosClient as jest.Mocked
- mockAxios.get.mockImplementationOnce(() =>
- Promise.resolve({
- status: 200,
- data: {status: 'failed'},
- }),
- )
- await expect(() =>
- createOrder({network: 'preprod', client: mockAxios}, createOrderParams),
- ).rejects.toThrowError(/^Unexpected error occurred$/)
- })
-
- it('should throw generic error for invalid response', async () => {
- const mockAxios = axiosClient as jest.Mocked
- await expect(async () => {
- mockAxios.get.mockImplementationOnce(() =>
- Promise.resolve({status: 400}),
- )
- await createOrder(
- {network: 'mainnet', client: mockAxios},
- createOrderParams,
- )
- }).rejects.toThrow('Failed to construct swap datum')
- })
- })
-
- describe('cancelOrder', () => {
- it('should cancel pending orders', async () => {
- const mockAxios = axiosClient as jest.Mocked
- mockAxios.get.mockImplementationOnce(() =>
- Promise.resolve({
- status: 200,
- data: {cbor: 'tx_cbor'},
- }),
- )
-
- const txCbor = await cancelOrder(
- {network: 'mainnet', client: mockAxios},
- {
- orderUTxO: 'orderUtxo',
- collateralUTxO: 'collateralUtxo',
- walletAddress:
- 'addr1q9ndnrwz52yeex4j04kggp0ul5632qmxqx22ugtukkytjysw86pdygc6zarl2kks6fvg8um447uvv679sfdtzkwf2kuq673wke',
- },
- )
-
- expect(txCbor).toBe('tx_cbor')
- })
-
- it('should throw generic error for invalid response', async () => {
- const mockAxios = axiosClient as jest.Mocked
- mockAxios.get.mockImplementationOnce(() => Promise.resolve({status: 400}))
- await expect(() =>
- cancelOrder(
- {network: 'mainnet', client: mockAxios},
- {
- orderUTxO: cancelOrderParams.utxo,
- collateralUTxO: cancelOrderParams.collateralUTxOs,
- walletAddress: cancelOrderParams.address,
- },
- ),
- ).rejects.toThrow('Failed to cancel swap transaction')
- })
- })
-})
-
-const mockedOrders = [
- {
- from: {
- amount: '1000000',
- token: '.',
- },
- to: {
- amount: '41372',
- token:
- '2adf188218a66847024664f4f63939577627a56c090f679fe366c5ee.535441424c45',
- },
- sender:
- 'addr1qy0556dz9jssrrnhv0g3ga98uczdd465cut9jjs5a4k5qy3yl52kwxsh5wfx3darrc4xwql43ylj2n29dpq3xg46a6mska8vfz',
- owner:
- 'addr1qy0556dz9jssrrnhv0g3ga98uczdd465cut9jjs5a4k5qy3yl52kwxsh5wfx3darrc4xwql43ylj2n29dpq3xg46a6mska8vfz',
- ownerPubKeyHash: '1f4a69a22ca1018e7763d11474a7e604d6d754c716594a14ed6d4012',
- ownerStakeKeyHash:
- '24fd15671a17a39268b7a31e2a6703f5893f254d4568411322baeeb7',
- batcherFee: {
- amount: '950000',
- token: '.',
- },
- deposit: '1700000',
- valueAttached: [
- {
- amount: '3650000',
- token: '.',
- },
- ],
- utxo: '1e977694e2413bd0e6105303bb44da60530cafe49b864dde8f8902b021ed86ba#0',
- provider: 'muesliswap_v4',
- feeField: '2650000',
- allowPartial: true,
- },
-]
-
-const mockedCreateOrderResult = {
- status: 'success',
- datum:
- 'd8799fd8799fd8799fd8799f581c353b8bc29a15603f0b73eac44653d1bd944d92e0e0dcd5eb185164a2ffd8799fd8799fd8799f581cda22c532206a75a628778eebaf63826f9d93fbe9b4ac69a7f8e4cd78ffffffff581c353b8bc29a15603f0b73eac44653d1bd944d92e0e0dcd5eb185164a21b00000188f2408726d8799fd8799f4040ffd8799f581cdda5fdb1002f7389b33e036b6afee82a8189becb6cba852e8b79b4fb480014df1047454e53ffffffd8799fd879801a0006517affff',
- hash: '4ae3fc5498e9d0f04daaf2ee739e41dc3f6f4119391e7274f0b3fa15aa2163ff',
- address: 'addr1wxr2a8htmzuhj39y2gq7ftkpxv98y2g67tg8zezthgq4jkg0a4ul4',
-}
-
-const createOrderParams = {
- walletAddress:
- 'addr1qy0556dz9jssrrnhv0g3ga98uczdd465cut9jjs5a4k5qy3yl52kwxsh5wfx3darrc4xwql43ylj2n29dpq3xg46a6mska8vfz',
- protocol: 'sundaeswap',
- poolId: '14',
- sell: {
- ...ADA_TOKEN,
- amount: '25000000',
- },
- buy: {
- ...GENS_TOKEN,
- amount: '50000000',
- },
-} as const
-
-const cancelOrderParams = {
- utxo: '6c4b4e55301d79128071f05a018cf05b7de86bc3f92d05b6668423e220152a86',
- collateralUTxOs:
- '6c4b4e55301d79128071f05a018cf05b7de86bc3f92d05b6668423e220152a86',
- address:
- 'addr1qy0556dz9jssrrnhv0g3ga98uczdd465cut9jjs5a4k5qy3yl52kwxsh5wfx3darrc4xwql43ylj2n29dpq3xg46a6mska8vfz',
-} as const
-
-const mockedCompleteOrders = [
- {
- status: 'matched',
- utxo: '6c4b4e55301d79128071f05a018cf05b7de86bc3f92d05b6668423e220152a86',
- collateralUTxOs:
- '6c4b4e55301d79128071f05a018cf05b7de86bc3f92d05b6668423e220152a86',
- address:
- 'addr1qy0556dz9jssrrnhv0g3ga98uczdd465cut9jjs5a4k5qy3yl52kwxsh5wfx3darrc4xwql43ylj2n29dpq3xg46a6mska8vfz',
- },
-] as const
diff --git a/packages/swap/src/adapters/openswap-api/orders.ts b/packages/swap/src/adapters/openswap-api/orders.ts
deleted file mode 100644
index 9a576e0a74..0000000000
--- a/packages/swap/src/adapters/openswap-api/orders.ts
+++ /dev/null
@@ -1,114 +0,0 @@
-import {SWAP_API_ENDPOINTS} from './config'
-import type {
- ApiDeps,
- CancelOrderRequest,
- CreateOrderRequest,
- CreateOrderResponse,
- CompletedOrderResponse,
- OpenOrderResponse,
-} from './types'
-
-export async function createOrder(
- deps: ApiDeps,
- args: CreateOrderRequest,
-): Promise {
- const {network, client} = deps
- const apiUrl = SWAP_API_ENDPOINTS[network].constructSwapDatum
- const response = await client.get<
- | {status: 'failed'; reason?: string}
- | {status: 'success'; hash: string; datum: string; address: string}
- >('/', {
- baseURL: apiUrl,
- params: {
- walletAddr: args.walletAddress,
- protocol: args.protocol,
- poolId: args.poolId,
- sellTokenPolicyID: args.sell.policyId,
- sellTokenNameHex: args.sell.assetName,
- sellAmount: args.sell.amount,
- buyTokenPolicyID: args.buy.policyId,
- buyTokenNameHex: args.buy.assetName,
- buyAmount: args.buy.amount,
- },
- })
-
- if (response.status !== 200) {
- throw new Error('Failed to construct swap datum', {
- cause: response.data,
- })
- }
-
- if (response.data.status === 'failed') {
- throw new Error(response.data.reason ?? 'Unexpected error occurred')
- }
-
- return response.data
-}
-
-export async function cancelOrder(
- deps: ApiDeps,
- args: CancelOrderRequest,
-): Promise {
- const {network, client} = deps
- const apiUrl = SWAP_API_ENDPOINTS[network].cancelSwapTransaction
- const response = await client.get('/', {
- baseURL: apiUrl,
- params: {
- wallet: args.walletAddress,
- utxo: args.orderUTxO,
- collateralUtxo: args.collateralUTxO,
- },
- })
-
- if (response.status !== 200) {
- throw new Error('Failed to cancel swap transaction', {
- cause: response.data,
- })
- }
-
- return response.data.cbor
-}
-
-export async function getOrders(
- deps: ApiDeps,
- args: {stakeKeyHash: string},
-): Promise {
- const {network, client} = deps
- const {stakeKeyHash} = args
- const apiUrl = SWAP_API_ENDPOINTS[network].getOrders
- const response = await client.get(apiUrl, {
- params: {
- 'stake-key-hash': stakeKeyHash,
- },
- })
-
- if (response.status !== 200) {
- throw new Error(`Failed to get orders for ${stakeKeyHash}`, {
- cause: response.data,
- })
- }
-
- return response.data
-}
-
-export async function getCompletedOrders(
- deps: ApiDeps,
- args: {stakeKeyHash: string},
-): Promise {
- const {network, client} = deps
- const {stakeKeyHash} = args
- const apiUrl = SWAP_API_ENDPOINTS[network].getCompletedOrders
- const response = await client.get(apiUrl, {
- params: {
- 'stake-key-hash': stakeKeyHash,
- },
- })
-
- if (response.status !== 200) {
- throw new Error(`Failed to get orders for ${stakeKeyHash}`, {
- cause: response.data,
- })
- }
-
- return response.data
-}
diff --git a/packages/swap/src/adapters/openswap-api/pools.test.ts b/packages/swap/src/adapters/openswap-api/pools.test.ts
deleted file mode 100644
index 3c473c1ffd..0000000000
--- a/packages/swap/src/adapters/openswap-api/pools.test.ts
+++ /dev/null
@@ -1,191 +0,0 @@
-import {getLiquidityPools, getPoolsPair} from './pools'
-import {axiosClient} from './config'
-import {LiquidityPoolResponse, PoolPairResponse} from './types'
-
-jest.mock('./config')
-
-describe('SwapPoolsApi', () => {
- describe('getLiquidityPools', () => {
- it('should get liquidity pools list for a given token pair', async () => {
- const mockAxios = axiosClient as jest.Mocked
- mockAxios.get.mockImplementationOnce(() =>
- Promise.resolve({
- status: 200,
- data: mockedLiquidityPoolsResponse,
- }),
- )
-
- const result = await getLiquidityPools(
- {network: 'mainnet', client: mockAxios},
- {
- tokenA: getLiquidityPoolsParams.sell,
- tokenB: getLiquidityPoolsParams.buy,
- providers: getLiquidityPoolsParams.providers,
- },
- )
- expect(result).toHaveLength(1)
- })
-
- it('should throw error for invalid response', async () => {
- const mockAxios = axiosClient as jest.Mocked
- await expect(async () => {
- mockAxios.get.mockImplementationOnce(() =>
- Promise.resolve({status: 500}),
- )
- await getLiquidityPools(
- {network: 'preprod', client: mockAxios},
- {
- tokenA: getLiquidityPoolsParams.sell,
- tokenB: getLiquidityPoolsParams.buy,
- providers: getLiquidityPoolsParams.providers,
- },
- )
- }).rejects.toThrow('Failed to fetch liquidity pools for token pair')
- })
- })
-
- describe('getPoolsPair', () => {
- it('should get pools pair list for a given token pair', async () => {
- const mockAxios = axiosClient as jest.Mocked
- mockAxios.get.mockImplementationOnce(() =>
- Promise.resolve({
- status: 200,
- data: mockedPoolsPairResponse,
- }),
- )
-
- const result = await getPoolsPair(
- {network: 'mainnet', client: mockAxios},
- {tokenA: getPoolsPairParams.sell, tokenB: getPoolsPairParams.buy},
- )
- expect(result).toHaveLength(1)
- })
-
- it('should throw error for invalid response', async () => {
- const mockAxios = axiosClient as jest.Mocked
- await expect(async () => {
- mockAxios.get.mockImplementationOnce(() =>
- Promise.resolve({status: 500}),
- )
- await getPoolsPair(
- {network: 'preprod', client: mockAxios},
- {tokenA: getPoolsPairParams.sell, tokenB: getPoolsPairParams.buy},
- )
- }).rejects.toThrow('Failed to fetch pools pair for token pair')
- })
- })
-})
-
-const mockedPoolsPairResponse: Readonly = [
- {
- provider: 'minswap',
- fee: '0.3',
- tokenA: {
- amount: '1233807687',
- token: '.',
- },
- tokenB: {
- amount: '780',
- token:
- 'e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed72.43414b45',
- },
- price: 0,
- batcherFee: {
- amount: '2000000',
- token: '.',
- },
- depositFee: {
- amount: '2000000',
- token: '.',
- },
- deposit: 2000000,
- utxo: '0596860b5970ef989c56f7ae38b3c0f74bb4979ac15ee994c30760f7f4d908ce#0',
- poolId:
- '0be55d262b29f564998ff81efe21bdc0022621c12f15af08d0f2ddb1.7339a8bcda85e2c997d9f16beddbeb3ad755f5202f5cfd9cb08db346a1292c01',
- timestamp: '2023-05-31 07:03:41',
- lpToken: {
- amount: '981004',
- token:
- 'e4214b7cce62ac6fbba385d164df48e157eae5863521b4b67ca71d86.7339a8bcda85e2c997d9f16beddbeb3ad755f5202f5cfd9cb08db346a1292c01',
- },
- batcherAddress:
- 'addr1wxaptpmxcxawvr3pzlhgnpmzz3ql43n2tc8mn3av5kx0yzs09tqh8',
- },
-]
-
-const getPoolsPairParams = {
- sell: {
- policyId: '',
- assetNameHex: '',
- },
- buy: {
- policyId: 'e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed72',
- assetNameHex: '43414b45',
- },
-} as const
-
-const mockedLiquidityPoolsResponse: Readonly = [
- {
- tokenA: {
- address: {
- policyId: '',
- name: '',
- },
- symbol: 'ADA',
- image: 'https://static.muesliswap.com/images/tokens/ada.png',
- decimalPlaces: 6,
- amount: '1000000',
- status: 'verified',
- priceAda: 1,
- },
- tokenB: {
- address: {
- policyId: '9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77',
- name: '53554e444145',
- },
- symbol: 'SUNDAE',
- image:
- 'https://tokens.muesliswap.com/static/img/tokens/9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77.53554e444145.png',
- decimalPlaces: 6,
- amount: '100000',
- status: 'verified',
- priceAda: 0.02567846556,
- },
- feeToken: {
- address: {
- policyId: '',
- name: '',
- },
- symbol: 'ADA',
- image: 'https://static.muesliswap.com/images/tokens/ada.png',
- decimalPlaces: 6,
- },
- batcherFee: '2500000',
- lvlDeposit: '2000000',
- poolFee: '1.00',
- lpToken: {
- address: {
- policyId: '0029cb7c88c7567b63d1a512c0ed626aa169688ec980730c0473b913',
- name: '6c7020dc',
- },
- amount: '316227',
- },
- poolId: '0029cb7c88c7567b63d1a512c0ed626aa169688ec980730c0473b913.7020dc',
- provider: 'sundaeswap',
- txHash: 'f2c5186fc53546db16a52c3bec25598e69518aaa8486919074c42e8927533f4c',
- outputIdx: 1,
- volume24h: 0,
- volume7d: 0,
- liquidityApy: 0,
- priceASqrt: null,
- priceBSqrt: null,
- batcherAddress:
- 'addr1wxaptpmxcxawvr3pzlhgnpmzz3ql43n2tc8mn3av5kx0yzs09tqh8',
- },
-]
-
-const getLiquidityPoolsParams = {
- sell: '',
- buy: 'e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed72.43414b45',
- providers: ['minswap'],
-} as const
diff --git a/packages/swap/src/adapters/openswap-api/pools.ts b/packages/swap/src/adapters/openswap-api/pools.ts
deleted file mode 100644
index 23b89fd41e..0000000000
--- a/packages/swap/src/adapters/openswap-api/pools.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-import {SWAP_API_ENDPOINTS} from './config'
-import type {
- ApiDeps,
- LiquidityPoolResponse,
- PoolPairResponse,
- Provider,
- TokenAddress,
-} from './types'
-
-export async function getLiquidityPools(
- deps: ApiDeps,
- args: {tokenA: string; tokenB: string; providers: ReadonlyArray},
-): Promise {
- const {tokenA, tokenB, providers} = args
- const {network, client} = deps
-
- const params: {[key: string]: string} = {
- 'token-a': tokenA,
- 'token-b': tokenB,
- 'providers': providers.join(','),
- }
-
- const apiUrl = SWAP_API_ENDPOINTS[network].getLiquidityPools
- const response = await client.get('', {
- baseURL: apiUrl,
- params,
- })
-
- if (response.status !== 200) {
- throw new Error('Failed to fetch liquidity pools for token pair', {
- cause: response.data,
- })
- }
-
- return response.data
-}
-
-export async function getPoolsPair(
- deps: ApiDeps,
- args: {tokenA: TokenAddress; tokenB: TokenAddress},
-): Promise {
- const {tokenA, tokenB} = args
- const {network, client} = deps
- const params: {[key: string]: string} = {
- 'policy-id1': tokenA.policyId,
- 'policy-id2': tokenB.policyId,
- }
-
- if ('assetName' in tokenA) params.tokenname1 = tokenA.assetName
- if ('assetName' in tokenB) params.tokenname2 = tokenB.assetName
-
- // note: {tokenname-hex} will overwrites {tokenname}
- if ('assetNameHex' in tokenA) params['tokenname-hex1'] = tokenA.assetNameHex
- if ('assetNameHex' in tokenB) params['tokenname-hex2'] = tokenB.assetNameHex
-
- const apiUrl = SWAP_API_ENDPOINTS[network].getPoolsPair
- const response = await client.get('', {
- baseURL: apiUrl,
- params,
- })
-
- if (response.status !== 200) {
- throw new Error('Failed to fetch pools pair for token pair', {
- cause: response.data,
- })
- }
-
- return response.data
-}
diff --git a/packages/swap/src/adapters/openswap-api/price.test.ts b/packages/swap/src/adapters/openswap-api/price.test.ts
deleted file mode 100644
index a13ef667a2..0000000000
--- a/packages/swap/src/adapters/openswap-api/price.test.ts
+++ /dev/null
@@ -1,97 +0,0 @@
-import {getPrice} from './price'
-import {axiosClient} from './config'
-import {PriceAddress, PriceResponse} from './types'
-
-jest.mock('./config')
-
-describe('SwapPoolsApi', () => {
- it('should get price for the pair token', async () => {
- const mockAxios = axiosClient as jest.Mocked
- mockAxios.get.mockImplementationOnce(() =>
- Promise.resolve({
- status: 200,
- data: mockedPriceResponse,
- }),
- )
-
- const result = await getPrice(
- {network: 'mainnet', client: mockAxios},
- {
- ...getPriceParams,
- },
- )
- expect(result).toEqual(mockedPriceResponse)
- })
-
- it('should throw error for invalid response', async () => {
- const mockAxios = axiosClient as jest.Mocked
- await expect(async () => {
- mockAxios.get.mockImplementationOnce(() => Promise.resolve({status: 500}))
- await getPrice(
- {network: 'preprod', client: mockAxios},
- {...getPriceParams},
- )
- }).rejects.toThrow('Failed to fetch price for token pair')
- })
-})
-
-const mockedPriceResponse: PriceResponse = {
- baseDecimalPlaces: 6,
- quoteDecimalPlaces: 6,
- baseAddress: {
- policyId: '',
- name: '',
- },
- quoteAddress: {
- policyId: '29d222ce763455e3d7a09a665ce554f00ac89d2e99a1a83d267170c6',
- name: '4d494e',
- },
- askPrice: 0.08209814208,
- bidPrice: 0.06319999985,
- price: 0.07080044463,
- volume: {
- base: '14735349',
- quote: '211287611',
- },
- volumeAggregator: {
- minswap: {
- quote: 107413106646,
- base: 7651672996,
- },
- sundaeswap: {
- quote: 566084169,
- base: 39000000,
- },
- vyfi: {
- quote: 12370434748,
- base: 879028993,
- },
- },
- volumeTotal: {
- base: 8584437338,
- quote: 120560913174,
- },
- volumeChange: {
- base: 0,
- quote: 0,
- },
- priceChange: {
- '24h': '-0.2374956426253183',
- '7d': '8.757469657697857',
- },
- marketCap: 68873484244745.086,
-} as const
-
-const getPriceParams: {
- baseToken: PriceAddress
- quoteToken: PriceAddress
-} = {
- baseToken: {
- policyId: '',
- name: '',
- },
- quoteToken: {
- policyId: '29d222ce763455e3d7a09a665ce554f00ac89d2e99a1a83d267170c6',
- name: '4d494e',
- },
-} as const
diff --git a/packages/swap/src/adapters/openswap-api/price.ts b/packages/swap/src/adapters/openswap-api/price.ts
deleted file mode 100644
index ba46cd1ce8..0000000000
--- a/packages/swap/src/adapters/openswap-api/price.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import {SWAP_API_ENDPOINTS} from './config'
-import type {ApiDeps, PriceAddress, PriceResponse} from './types'
-
-export async function getPrice(
- deps: ApiDeps,
- args: {baseToken: PriceAddress; quoteToken: PriceAddress},
-): Promise {
- const {baseToken, quoteToken} = args
- const {network, client} = deps
- const params: {[key: string]: string} = {
- 'base-policy-id': baseToken.policyId,
- 'base-token-name': baseToken.policyId,
- 'quote-policy-id': quoteToken.policyId,
- 'quote-token-name': quoteToken.policyId,
- }
-
- const apiUrl = SWAP_API_ENDPOINTS[network].getPrice
- const response = await client.get('', {
- baseURL: apiUrl,
- params,
- })
-
- if (response.status !== 200) {
- throw new Error('Failed to fetch price for token pair', {
- cause: response.data,
- })
- }
-
- return response.data
-}
diff --git a/packages/swap/src/adapters/openswap-api/token-pairs.test.ts b/packages/swap/src/adapters/openswap-api/token-pairs.test.ts
deleted file mode 100644
index b07af955cd..0000000000
--- a/packages/swap/src/adapters/openswap-api/token-pairs.test.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-import {getTokenPairs} from './token-pairs'
-import {axiosClient} from './config'
-
-jest.mock('./config.ts')
-
-describe('SwapTokenPairsApi', () => {
- it('should get all tokens based pairs', async () => {
- const mockAxios = axiosClient as jest.Mocked
- mockAxios.get.mockImplementationOnce(() =>
- Promise.resolve({status: 200, data: mockedGetTokenPairsResponse}),
- )
-
- const result = await getTokenPairs({network: 'mainnet', client: mockAxios})
-
- expect(result).toHaveLength(1)
- })
-
- it('should return empty list on preprod network', async () => {
- const mockAxios = axiosClient as jest.Mocked
-
- const result = await getTokenPairs({network: 'preprod', client: mockAxios})
-
- expect(result).toHaveLength(0)
- })
-
- it('should throw error for invalid response', async () => {
- const mockAxios = axiosClient as jest.Mocked
- mockAxios.get.mockImplementationOnce(() => Promise.resolve({status: 500}))
- await expect(() =>
- getTokenPairs({network: 'mainnet', client: mockAxios}),
- ).rejects.toThrow('Failed to fetch token pairs')
- })
-})
-
-const mockedGetTokenPairsResponse = [
- {
- info: {
- supply: {total: '1000000000000', circulating: null},
- status: 'unverified',
- image: 'ipfs://QmPzaykTy4yfutCtwv7nRUmgbQbA7euiThyy2i9fiFuDHX',
- imageIpfsHash: 'QmPzaykTy4yfutCtwv7nRUmgbQbA7euiThyy2i9fiFuDHX',
- symbol: 'ARGENT',
- minting: {
- type: 'time-lock-policy',
- blockchain: 'cardano',
- mintedBeforeSlotNumber: 91850718,
- },
- mediatype: 'image/png',
- tokentype: 'token',
- description: 'ARGENT Token',
- totalsupply: 1000000000000,
- address: {
- policyId: 'c04f4200502a998e9eebafac0291a1f38008de3fe146d136946d8f4b',
- name: '415247454e54',
- },
- decimalPlaces: 0,
- categories: [],
- },
- price: {
- volume: {base: 0, quote: 0},
- volumeChange: {base: 0, quote: 0},
- volumeTotal: {base: 0, quote: 0},
- volumeAggregator: {},
- price: 0,
- askPrice: 0,
- bidPrice: 0,
- priceChange: {'24h': 0, '7d': 0},
- quoteDecimalPlaces: 0,
- baseDecimalPlaces: 6,
- quoteAddress: {
- policyId: 'c04f4200502a998e9eebafac0291a1f38008de3fe146d136946d8f4b',
- name: '415247454e54',
- },
- baseAddress: {policyId: '', name: ''},
- price10d: [],
- },
- },
-]
diff --git a/packages/swap/src/adapters/openswap-api/token-pairs.ts b/packages/swap/src/adapters/openswap-api/token-pairs.ts
deleted file mode 100644
index d6aaf46d2e..0000000000
--- a/packages/swap/src/adapters/openswap-api/token-pairs.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import {SWAP_API_ENDPOINTS} from './config'
-import type {ApiDeps, TokenPairsResponse} from './types'
-
-export async function getTokenPairs(
- deps: ApiDeps,
- {policyId = '', assetName = ''} = {},
-): Promise {
- const {network, client} = deps
- if (network === 'preprod') return []
-
- const apiUrl = SWAP_API_ENDPOINTS[network].getTokenPairs
- const response = await client.get('', {
- baseURL: apiUrl,
- params: {
- 'base-policy-id': policyId,
- 'base-tokenname': assetName,
- },
- })
-
- if (response.status !== 200) {
- throw new Error('Failed to fetch token pairs', {cause: response.data})
- }
-
- return response.data
-}
diff --git a/packages/swap/src/adapters/openswap-api/tokens.test.ts b/packages/swap/src/adapters/openswap-api/tokens.test.ts
deleted file mode 100644
index b660bf015d..0000000000
--- a/packages/swap/src/adapters/openswap-api/tokens.test.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import {getTokens} from './tokens'
-import {axiosClient} from './config'
-
-jest.mock('./config.ts')
-
-describe('SwapTokensApi', () => {
- it('should get all supported tokens list', async () => {
- const mockAxios = axiosClient as jest.Mocked
- mockAxios.get.mockImplementationOnce(() =>
- Promise.resolve({status: 200, data: mockedGetTokensResponse}),
- )
-
- const result = await getTokens({network: 'mainnet', client: mockAxios})
-
- expect(result).toHaveLength(1)
- })
-
- it('should throw error for invalid response', async () => {
- const mockAxios = axiosClient as jest.Mocked
- mockAxios.get.mockImplementationOnce(() => Promise.resolve({status: 500}))
-
- await expect(() =>
- getTokens({network: 'mainnet', client: mockAxios}),
- ).rejects.toThrow('Failed to fetch tokens')
- })
-
- it('should return empty array', async () => {
- const mockAxios = axiosClient as jest.Mocked
- mockAxios.get.mockImplementationOnce(() =>
- Promise.resolve({status: 200, data: mockedGetTokensResponse}),
- )
-
- const result = await getTokens({network: 'preprod', client: mockAxios})
-
- expect(result).toHaveLength(0)
- })
-})
-
-const mockedGetTokensResponse = [
- {
- supply: {
- total: '10000000',
- circulating: '6272565',
- },
- status: 'verified',
- website: 'https://ada.muesliswap.com/',
- symbol: 'MILK',
- decimalPlaces: 0,
- image: 'https://static.muesliswap.com/images/tokens/MILK.png',
- description: 'MILK is the utility token powering the MuesliSwap ecosystem.',
- address: {
- policyId: '8a1cfae21368b8bebbbed9800fec304e95cce39a2a57dc35e2e3ebaa',
- name: '4d494c4b',
- },
- categories: ['1', '2'],
- },
-]
diff --git a/packages/swap/src/adapters/openswap-api/tokens.ts b/packages/swap/src/adapters/openswap-api/tokens.ts
deleted file mode 100644
index e1fc16909c..0000000000
--- a/packages/swap/src/adapters/openswap-api/tokens.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import {SWAP_API_ENDPOINTS} from './config'
-import type {ApiDeps, ListTokensResponse} from './types'
-
-export async function getTokens(deps: ApiDeps): Promise {
- const {network, client} = deps
- if (network === 'preprod') return []
-
- const apiUrl = SWAP_API_ENDPOINTS[network].getTokens
- const response = await client.get('', {
- baseURL: apiUrl,
- })
-
- if (response.status !== 200) {
- throw new Error('Failed to fetch tokens', {cause: response.data})
- }
-
- return response.data
-}
diff --git a/packages/swap/src/adapters/openswap-api/types.ts b/packages/swap/src/adapters/openswap-api/types.ts
deleted file mode 100644
index 951f0327eb..0000000000
--- a/packages/swap/src/adapters/openswap-api/types.ts
+++ /dev/null
@@ -1,279 +0,0 @@
-/* istanbul ignore file */
-
-import {AxiosInstance} from 'axios'
-
-export type CancelOrderRequest = {
- orderUTxO: string // order UTxO from the smart contract to cancel. e.g. "txhash#0".
- collateralUTxO: string // collateral UTxOs to use for canceling the order in cbor format.
- walletAddress: string // address of the wallet that owns the order in cbor format.
-}
-
-export type CreateOrderRequest = {
- walletAddress: string
- protocol: Provider // only in the CreateOrder they call provider as protocol
- poolId?: string // only required for SundaeSwap trades.
- sell: {
- policyId: string
- assetName: string // hexadecimal representation of token, i.e. "" for lovelace, "4d494c4b" for MILK.
- amount: string
- }
- buy: {
- policyId: string
- assetName: string // hexadecimal representation of token, i.e. "" for lovelace, "4d494c4b" for MILK.
- amount: string
- }
-}
-
-export type CreateOrderResponse =
- | {status: 'failed'; reason?: string}
- | {status: 'success'; hash: string; datum: string; address: string}
-
-export type OpenOrder = {
- provider: Provider
- owner: string
- from: {
- amount: string
- token: string
- }
- to: {
- amount: string
- token: string
- }
- deposit: string
- utxo: string
-}
-export type OpenOrderResponse = OpenOrder[]
-
-export type CompletedOrder = {
- toToken: {
- address: {
- policyId: string
- name: string
- }
- }
- toAmount: string
- fromToken: {
- address: {
- policyId: string
- name: string
- }
- }
- fromAmount: string
- placedAt: number
- status: string
- receivedAmount: string
- paidAmount: string
- finalizedAt: any
- txHash: string
- outputIdx: number
- attachedLvl: string
- scriptVersion: string
- pubKeyHash: string
- dex: Provider
-}
-export type CompletedOrderResponse = CompletedOrder[]
-
-export type Provider =
- | 'minswap'
- | 'sundaeswap'
- | 'wingriders'
- | 'muesliswap'
- | 'muesliswap_v1'
- | 'muesliswap_v2'
- | 'muesliswap_v3'
- | 'muesliswap_v4'
- | 'vyfi'
- | 'spectrum'
-// | 'muesliswap_clp'
-
-export type Network = 'mainnet' | 'preprod'
-
-// NOTE: TBR
-export type PoolPair = {
- provider: Provider
- fee: string // % pool liquidity provider fee, usually 0.3.
- tokenA: {
- amount: string // amount of tokenA in the pool, without decimals.
- token: string // hexadecimal representation of tokenA, i.e. "." for lovelace, "8a1cfae21368b8bebbbed9800fec304e95cce39a2a57dc35e2e3ebaa.4d494c4b" for MILK.
- }
- tokenB: {
- amount: string // amount of tokenB in the pool, without decimals.
- token: string // hexadecimal representation of tokenB, i.e. "." for lovelace, "8a1cfae21368b8bebbbed9800fec304e95cce39a2a57dc35e2e3ebaa.4d494c4b" for MILK.
- }
- price: number // float, current price in tokenA / tokenB according to the pool, NOT SUITABLE for price calculations, just for display purposes, i.e. 0.9097362621640215.
- batcherFee: {
- amount: string // amount of fee taken by protocol batchers, in lovelace.
- token: string // most likely "." for lovelace.
- }
- deposit: number // amount of deposit / minUTxO required by protocol, returned to user, in lovelace.
- utxo: string // txhash#txindex of latest transaction involving this pool.
- poolId: string // identifier of the pool across platforms.
- timestamp: string // latest update of this pool in UTC, i.e. 2023-05-23 06:13:26.
- lpToken: {
- amount: string // amount of lpToken minted by the pool, without decimals.
- token: string // hexadecimal representation of lpToken,
- }
- depositFee: {
- amount: string // amount of fee taken by protocol batchers, in lovelace.
- token: string // most likely "." for lovelace.
- }
- batcherAddress: string // address of the protocol batcher.
-}
-export type PoolPairResponse = PoolPair[]
-
-export type TokenPair = {
- info: {
- supply: {
- total: string // total circulating supply of the token, without decimals.
- circulating: string | null // if set the circulating supply of the token, if null the amount in circulation is unknown.
- }
- status: 'verified' | 'unverified' | 'scam' | 'outdated'
- address: {
- policyId: string // policy id of the token.
- name: string // hexadecimal representation of token name.
- }
- symbol: string // shorthand token symbol.
- image?: string // http link to the token image.
- website: string
- description: string
- decimalPlaces: number // number of decimal places of the token, i.e. 6 for ADA and 0 for MILK.
- categories: string[] // encoding categories as ids.
- sign?: string // token sign, i.e. "₳" for ADA.
- }
- price: {
- volume: {
- base: string // float, trading volume 24h in base currency (e.g. ADA).
- quote: string // float, trading volume 24h in quote currency.
- }
- volumeChange: {
- base: number // float, percent change of trading volume in comparison to previous 24h.
- quote: number // float, percent change of trading volume in comparison to previous 24h.
- }
- price: number // live trading price in base currency (e.g. ADA).
- askPrice: number // lowest ask price in base currency (e.g. ADA).
- bidPrice: number // highest bid price in base currency (e.g. ADA).
- priceChange: {
- '24h': string // float, price change last 24 hours.
- '7d': string // float, price change last 7 days.
- }
- quoteDecimalPlaces: number // decimal places of quote token.
- baseDecimalPlaces: number // decimal places of base token.
- price10d: number[] //float, prices of this tokens averaged for the last 10 days, in chronological order i.e.oldest first.
- }
-}
-export type TokenPairsResponse = TokenPair[]
-
-export type TokenInfo = Omit
-export type ListTokensResponse = TokenInfo[]
-
-export type TokenAddress =
- | {
- policyId: string
- assetName: string
- }
- | {
- policyId: string
- assetNameHex: string
- }
-
-export type ApiDeps = {
- network: Network
- client: AxiosInstance
-}
-
-export type PriceAddress = {
- policyId: string
- name: string
-}
-
-type VolumeAggregator = {
- [key in Provider]?: {
- quote: number
- base: number
- }
-}
-
-export type PriceResponse = {
- baseDecimalPlaces: number
- quoteDecimalPlaces: number
- baseAddress: PriceAddress
- quoteAddress: PriceAddress
- askPrice: number
- bidPrice: number
- price: number
- volume: {
- base: string
- quote: string
- }
- volumeAggregator: VolumeAggregator
- volumeTotal: {
- base: number
- quote: number
- }
- volumeChange: {
- base: number
- quote: number
- }
- priceChange: {
- '24h': string
- '7d': string
- }
- marketCap: number
-}
-
-export type LiquidityPoolResponse = LiquidityPool[]
-export type LiquidityPool = {
- tokenA: {
- address: {
- policyId: string
- name: string
- }
- symbol?: string
- image?: string
- decimalPlaces: number
- amount: string
- status: string
- priceAda: number
- }
- tokenB: {
- address: {
- policyId: string
- name: string
- }
- symbol?: string
- image?: string
- decimalPlaces: number
- amount: string
- status: string
- priceAda: number
- }
- feeToken: {
- address: {
- policyId: string
- name: string
- }
- symbol?: string
- image?: string
- decimalPlaces: number
- }
- batcherFee: string
- lvlDeposit: string
- poolFee: string
- lpToken: {
- address?: {
- policyId: string
- name: string
- }
- amount?: string
- }
- poolId: string
- provider: Provider
- txHash?: string
- outputIdx?: number
- volume24h?: number
- volume7d?: number
- liquidityApy?: number
- priceASqrt?: any
- priceBSqrt?: any
- batcherAddress: string
-}
diff --git a/packages/swap/src/helpers/transformers.ts b/packages/swap/src/helpers/transformers.ts
index a1de47bff8..2e39d0f238 100644
--- a/packages/swap/src/helpers/transformers.ts
+++ b/packages/swap/src/helpers/transformers.ts
@@ -1,24 +1,7 @@
import AssetFingerprint from '@emurgo/cip14-js'
-import {Swap, Balance, Portfolio} from '@yoroi/types'
-import {isString} from '@yoroi/common'
import {AssetNameUtils} from '@emurgo/yoroi-lib/dist/internals/utils/assets'
-import {Quantities} from '../utils/quantities'
-import {supportedProviders} from '../translators/constants'
-import {asQuantity} from '../utils/asQuantity'
-import {
- CompletedOrder,
- LiquidityPool,
- ListTokensResponse,
- OpenOrder,
- TokenPair,
- TokenPairsResponse,
-} from '../adapters/openswap-api/types'
-
-const asPolicyIdAndAssetName = (tokenId: string): [string, string] => {
- return tokenId.split('.') as [string, string]
-}
-
+/*
export const transformersMaker = (primaryTokenInfo: Portfolio.Token.Info) => {
const asOpenswapTokenId = (yoroiTokenId: string) => {
const [policyId, assetName] = asPolicyIdAndAssetName(yoroiTokenId)
@@ -255,12 +238,6 @@ export const transformersMaker = (primaryTokenInfo: Portfolio.Token.Info) => {
return yoroiAmount
}
- /**
- * Filter out pools that are not supported by Yoroi
- *
- * @param openswapLiquidityPools
- * @returns {Swap.Pool[]}
- */
const asYoroiPools = (
openswapLiquidityPools: LiquidityPool[],
): Swap.Pool[] => {
@@ -292,6 +269,7 @@ export const transformersMaker = (primaryTokenInfo: Portfolio.Token.Info) => {
asYoroiPortfolioTokenInfosFromPairs,
}
}
+*/
export const asTokenFingerprint = ({
policyId,
@@ -311,9 +289,3 @@ export const asTokenName = (hex: string) => {
const {asciiName, hexName} = AssetNameUtils.resolveProperties(hex)
return asciiName ?? hexName
}
-
-function isSupportedProvider(
- provider: string,
-): provider is Swap.SupportedProvider {
- return supportedProviders.includes(provider as Swap.SupportedProvider)
-}
diff --git a/packages/swap/src/index.ts b/packages/swap/src/index.ts
index c66df66340..1f2374ef26 100644
--- a/packages/swap/src/index.ts
+++ b/packages/swap/src/index.ts
@@ -46,7 +46,8 @@ export {useSwap} from './translators/reactjs/hooks/useSwap'
export {supportedProviders, milkTokenId} from './translators/constants'
// factories
-export {swapApiMaker} from './adapters/api-maker'
+export {swapApiMaker} from './adapters/openswap-api/api-maker'
+export {dexhunterApiMaker} from './adapters/api/dexhunter/api-maker'
export {swapManagerMaker} from './manager'
export {
swapStorageMaker,
diff --git a/packages/types/src/api/app.ts b/packages/types/src/api/app.ts
index 77d9c3c3eb..596acae065 100644
--- a/packages/types/src/api/app.ts
+++ b/packages/types/src/api/app.ts
@@ -1,5 +1,5 @@
import {BalanceQuantity} from '../balance/token'
-import {SwapAggregator} from '../swap/aggregator'
+import {SwapAggregator} from '../swap/api'
export interface AppApi {
getFrontendFees(): Promise
diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts
index 1ad9190454..88b18e2a47 100644
--- a/packages/types/src/index.ts
+++ b/packages/types/src/index.ts
@@ -5,23 +5,21 @@ import {
BalanceQuantity,
BalanceToken,
} from './balance/token'
-import {SwapApi} from './swap/api'
-import {SwapProtocol} from './swap/protocol'
import {
- SwapCancelOrderData,
- SwapCompletedOrder,
- SwapCreateOrderData,
- SwapCreateOrderResponse,
- SwapOpenOrder,
- SwapOrderType,
-} from './swap/order'
-import {SwapPool, SwapPoolProvider, SwapSupportedProvider} from './swap/pool'
-import {SwapStorage} from './swap/storage'
-import {SwapManager} from './swap/manager'
+ SwapAggregator,
+ SwapApi,
+ SwapCancelRequest,
+ SwapCancelResponse,
+ SwapCreateRequest,
+ SwapCreateResponse,
+ SwapEstimateRequest,
+ SwapEstimateResponse,
+ SwapOrder,
+ SwapSplit,
+} from './swap/api'
import {AppStorage, AppStorageFolderName} from './app/storage'
import {AppMultiStorage, AppMultiStorageOptions} from './app/multi-storage'
import {NumberLocale} from './intl/numbers'
-import {SwapAggregator} from './swap/aggregator'
import {
ResolverAddressesResponse,
ResolverAddressResponse,
@@ -270,11 +268,8 @@ import {
NotificationTransactionReceivedEvent,
NotificationTrigger,
} from './notifications/manager'
-import {
- SwapMakeOrderCalculation,
- SwapOrderCalculation,
-} from './swap/calculations'
import {NumbersRatio} from './numbers/ratio'
+import {SwapStorage} from './swap/storage'
export namespace App {
export namespace Errors {
@@ -340,7 +335,21 @@ export namespace App {
}
export namespace Swap {
- export interface Api extends SwapApi {}
+ export type Api = SwapApi
+ export type Order = SwapOrder
+ export type Aggregator = SwapAggregator
+ export const Aggregator = SwapAggregator
+ export type CancelRequest = SwapCancelRequest
+ export type CancelResponse = SwapCancelResponse
+ export type EstimateRequest = SwapEstimateRequest
+ export type EstimateResponse = SwapEstimateResponse
+ export type CreateRequest = SwapCreateRequest
+ export type CreateResponse = SwapCreateResponse
+ export type Split = SwapSplit
+
+ export type Storage = SwapStorage
+
+ /*
export type Manager = SwapManager
export type OpenOrder = SwapOpenOrder
@@ -362,11 +371,11 @@ export namespace Swap {
export type PoolProvider = SwapPoolProvider
export type SupportedProvider = SwapSupportedProvider
- export type Storage = SwapStorage
export type MakeOrderCalculation = SwapMakeOrderCalculation
export type OrderCalculation = SwapOrderCalculation
+ */
}
export namespace Balance {
diff --git a/packages/types/src/swap/aggregator.ts b/packages/types/src/swap/aggregator.ts
deleted file mode 100644
index c7427a1765..0000000000
--- a/packages/types/src/swap/aggregator.ts
+++ /dev/null
@@ -1 +0,0 @@
-export type SwapAggregator = 'muesliswap' | 'dexhunter'
diff --git a/packages/types/src/swap/api.ts b/packages/types/src/swap/api.ts
index ba1543b357..e0756d1840 100644
--- a/packages/types/src/swap/api.ts
+++ b/packages/types/src/swap/api.ts
@@ -1,33 +1,133 @@
+import {ApiResponse} from '../api/response'
import {PortfolioTokenInfo} from '../portfolio/info'
import {PortfolioTokenId} from '../portfolio/token'
-import {
- SwapCancelOrderData,
- SwapCompletedOrder,
- SwapCreateOrderData,
- SwapCreateOrderResponse,
- SwapOpenOrder,
-} from './order'
-import {SwapPool, SwapPoolProvider} from './pool'
-
-export interface SwapApi {
- createOrder(orderData: SwapCreateOrderData): Promise
- cancelOrder(orderData: SwapCancelOrderData): Promise
- getOpenOrders(): Promise
- getCompletedOrders(): Promise
- getPools(args: {
- tokenA: PortfolioTokenId
- tokenB: PortfolioTokenId
- providers?: ReadonlyArray
- }): Promise
- getTokenPairs(
- tokenIdBase: PortfolioTokenId,
- ): Promise>
- getTokens(): Promise>
- getPrice(args: {
- baseToken: PortfolioTokenId
- quoteToken: PortfolioTokenId
- }): Promise
- stakingKey: string
- primaryTokenInfo: Readonly
- supportedProviders: ReadonlyArray
+
+export const SwapAggregator = {
+ Muesliswap: 'muesliswap',
+ Dexhunter: 'dexhunter',
+} as const
+export type SwapAggregator =
+ (typeof SwapAggregator)[keyof typeof SwapAggregator]
+
+export type SwapOrder = {
+ aggregator: SwapAggregator
+ dex: string
+ placedAt?: number
+ lastUpdate?: number
+ status: string
+ tokenIn: PortfolioTokenId
+ tokenOut: PortfolioTokenId
+ amountIn: number
+ actualAmountOut: number
+ expectedAmountOut: number
+ txHash?: string
+ outputIndex?: number
+ updateTxHash?: string
+ customId?: string
+}
+
+export type SwapEstimateRequest = {
+ slippage: number
+ tokenIn: PortfolioTokenId
+ tokenOut: PortfolioTokenId
+ dex?: string
+ blacklistedDexes?: string[]
+} & (
+ | {
+ amountOut?: undefined
+ amountIn: number
+ multiples?: number
+ wantedPrice?: number
+ }
+ | {
+ amountOut: number
+ amountIn?: undefined
+ multiples?: undefined
+ wantedPrice?: undefined
+ }
+)
+
+export type SwapSplit = {
+ amountIn: number
+ batcherFee: number
+ deposits: number
+ dex: string
+ expectedOutput: number
+ expectedOutputWithoutSlippage: number
+ fee: number
+ finalPrice: number
+ initialPrice: number
+ poolFee: number
+ poolId: string
+ priceDistortion: number
+ priceImpact: number
+}
+
+export type SwapEstimateResponse = {
+ splits: SwapSplit[]
+ batcherFee: number
+ deposits: number
+ aggregatorFee: number
+ frontendFee: number
+ netPrice: number
+ totalFee: number
+ totalOutput: number
+ totalOutputWithoutSlippage?: number
+ totalInput?: number
+}
+
+export type SwapCreateRequest = {
+ amountIn: number
+ tokenIn: PortfolioTokenId
+ tokenOut: PortfolioTokenId
+ dex?: string
+ blacklistedDexes?: string[]
+} & (
+ | {
+ multiples?: number
+ wantedPrice?: number
+ slippage?: undefined
+ }
+ | {
+ slippage: number
+ wantedPrice?: undefined
+ multiples?: undefined
+ }
+)
+
+export type SwapCreateResponse = {
+ cbor: string
+ splits: SwapSplit[]
+ batcherFee: number
+ deposits: number
+ aggregatorFee: number
+ frontendFee: number
+ netPrice?: number
+ totalFee: number
+ totalInput: number
+ totalOutput: number
+ totalOutputWithoutSlippage?: number
+}
+
+export type SwapCancelRequest = {
+ order: SwapOrder
+ collateral?: string
+}
+
+export type SwapCancelResponse = {
+ cbor: string
+ additionalCancellationFee?: number
}
+export type SwapApi = Readonly<{
+ orders: () => Promise>>>
+ tokens: () => Promise>>>
+ estimate(
+ args: SwapEstimateRequest,
+ ): Promise>>
+ create(
+ args: SwapCreateRequest,
+ ): Promise>>
+ cancel: (
+ args: SwapCancelRequest,
+ ) => Promise>>
+}>
diff --git a/packages/types/src/swap/calculations.ts b/packages/types/src/swap/calculations.ts
index 25513f6708..867bb897af 100644
--- a/packages/types/src/swap/calculations.ts
+++ b/packages/types/src/swap/calculations.ts
@@ -1,3 +1,4 @@
+/*
import {App, Portfolio, Swap} from '..'
export type SwapMakeOrderCalculation = Readonly<{
@@ -60,3 +61,4 @@ export type SwapOrderCalculation = Readonly<{
ptTotalRequired: Portfolio.Token.Amount
}
}>
+*/
diff --git a/packages/types/src/swap/manager.ts b/packages/types/src/swap/manager.ts
index 9d1a41a9b8..cc805acfd4 100644
--- a/packages/types/src/swap/manager.ts
+++ b/packages/types/src/swap/manager.ts
@@ -1,3 +1,4 @@
+/*
import {AppFrontendFeeTier} from '../api/app'
import {PortfolioTokenInfo} from '../portfolio/info'
import {PortfolioTokenId} from '../portfolio/token'
@@ -52,3 +53,4 @@ export type SwapManager = Readonly<{
bestPoolCalculation?: SwapOrderCalculation
}): SwapOrderCalculation | undefined
}>
+*/
diff --git a/packages/types/src/swap/order.ts b/packages/types/src/swap/order.ts
index 0151421eeb..fd521b28b8 100644
--- a/packages/types/src/swap/order.ts
+++ b/packages/types/src/swap/order.ts
@@ -1,3 +1,4 @@
+/*
import {BalanceQuantity} from '../balance/token'
import {PortfolioTokenId} from '../portfolio/token'
import {SwapPool, SwapPoolProvider} from './pool'
@@ -66,3 +67,4 @@ export type SwapCompletedOrder = {
provider: SwapPoolProvider
placedAt: number
}
+*/
diff --git a/packages/types/src/swap/pool.ts b/packages/types/src/swap/pool.ts
index b08dc34119..39163281a7 100644
--- a/packages/types/src/swap/pool.ts
+++ b/packages/types/src/swap/pool.ts
@@ -1,3 +1,4 @@
+/*
import {PortfolioTokenId} from '../portfolio/token'
export type SwapPoolProvider =
@@ -51,3 +52,4 @@ export type SwapPool = {
quantity: bigint
}
}
+*/
diff --git a/packages/types/src/swap/protocol.ts b/packages/types/src/swap/protocol.ts
index a5af495f01..1512276444 100644
--- a/packages/types/src/swap/protocol.ts
+++ b/packages/types/src/swap/protocol.ts
@@ -1,5 +1,7 @@
+/*
export type SwapProtocol =
| 'minswap'
| 'sundaeswap'
| 'wingriders'
| 'muesliswap'
+*/
diff --git a/yarn.lock b/yarn.lock
index 2ba634c17a..170c8a9b15 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5883,6 +5883,18 @@
dependencies:
"@types/node" "*"
+"@types/lodash-es@^4.17.12":
+ version "4.17.12"
+ resolved "https://registry.yarnpkg.com/@types/lodash-es/-/lodash-es-4.17.12.tgz#65f6d1e5f80539aa7cfbfc962de5def0cf4f341b"
+ integrity sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==
+ dependencies:
+ "@types/lodash" "*"
+
+"@types/lodash@*":
+ version "4.17.13"
+ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.13.tgz#786e2d67cfd95e32862143abe7463a7f90c300eb"
+ integrity sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==
+
"@types/lodash@^4.14.175":
version "4.14.195"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.195.tgz#bafc975b252eb6cea78882ce8a7b6bf22a6de632"
@@ -15877,6 +15889,11 @@ locate-path@^6.0.0:
dependencies:
p-locate "^5.0.0"
+lodash-es@^4.17.21:
+ version "4.17.21"
+ resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
+ integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
+
lodash.camelcase@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
@@ -21307,7 +21324,16 @@ string-natural-compare@^3.0.1:
resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4"
integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==
-"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
+"string-width-cjs@npm:string-width@^4.2.0":
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+ integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+ dependencies:
+ emoji-regex "^8.0.0"
+ is-fullwidth-code-point "^3.0.0"
+ strip-ansi "^6.0.1"
+
+"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -21420,7 +21446,7 @@ string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"
-"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -21448,6 +21474,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
dependencies:
ansi-regex "^4.1.0"
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+ integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+ dependencies:
+ ansi-regex "^5.0.1"
+
strip-ansi@^7.0.1:
version "7.1.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
@@ -23070,7 +23103,7 @@ workerpool@6.2.1:
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343"
integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==
-"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
+"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@@ -23106,6 +23139,15 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"
+wrap-ansi@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+ integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+ dependencies:
+ ansi-styles "^4.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"