@yoroi/swap
Version:
The Swap package of Yoroi SDK
367 lines (366 loc) • 13 kB
JavaScript
"use strict";
import { fetchData } from '@yoroi/common';
import { Api, Chain } from '@yoroi/types';
import { dexhunterApiMaker } from './api-maker';
import { api } from './api.mocks';
jest.mock('@yoroi/common', () => ({
fetchData: jest.fn(),
isLeft: jest.requireActual('@yoroi/common').isLeft,
difference: jest.requireActual('@yoroi/common').difference
}));
describe('dexhunterApiMaker', () => {
const mockFetchData = fetchData;
const config = {
address: 'someAddress',
primaryTokenInfo: {},
isPrimaryToken: () => false,
partner: 'somePartnerId',
network: Chain.Network.Mainnet
// request defaults to fetchData, so we don't need to provide it explicitly
};
afterEach(() => {
mockFetchData.mockReset();
});
it('should return an object with the Swap.Api interface', () => {
const dhApi = dexhunterApiMaker(config);
expect(dhApi).toHaveProperty('tokens');
expect(dhApi).toHaveProperty('orders');
expect(dhApi).toHaveProperty('limitOptions');
expect(dhApi).toHaveProperty('estimate');
expect(dhApi).toHaveProperty('create');
expect(dhApi).toHaveProperty('cancel');
});
it('should return error if network is not Mainnet', async () => {
const testConfig = {
...config,
network: Chain.Network.Preprod
};
const dhApi = dexhunterApiMaker(testConfig);
const result = await dhApi.tokens();
if (result.tag !== 'left') fail();
expect(result.tag).toBe('left');
expect(result.error.message).toMatch(/only works on mainnet/);
});
describe('tokens()', () => {
it('should return a successful transformed response', async () => {
mockFetchData.mockResolvedValueOnce({
tag: 'right',
value: {
status: Api.HttpStatusCode.Ok,
data: api.responses.tokens
}
});
const dhApi = dexhunterApiMaker(config);
const result = await dhApi.tokens();
expect(mockFetchData).toHaveBeenCalledWith({
method: 'get',
url: 'https://api-us.dexhunterv3.app/swap/tokens',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-Partner-Id': 'somePartnerId'
}
});
expect(result.tag).toBe('right');
});
it('should return a left (error) if Dexhunter fails', async () => {
mockFetchData.mockResolvedValueOnce({
tag: 'left',
error: {
status: 500,
message: 'Server error',
responseData: {
detail: 'Tokens error'
}
}
});
const dhApi = dexhunterApiMaker(config);
const result = await dhApi.tokens();
if (result.tag !== 'left') fail();
expect(result.tag).toBe('left');
expect(result.error.message).toContain('Tokens error');
});
it('should return a left (error) if response is not JSON', async () => {
mockFetchData.mockResolvedValueOnce({
tag: 'left',
error: {
status: 500,
message: '',
responseData: null
}
});
const dhApi = dexhunterApiMaker(config);
const result = await dhApi.tokens();
if (result.tag !== 'left') fail();
expect(result.tag).toBe('left');
expect(result.error.message).toContain('Dexhunter API error');
});
});
describe('orders()', () => {
it('should return a successful transformed response', async () => {
mockFetchData.mockResolvedValueOnce({
tag: 'right',
value: {
status: Api.HttpStatusCode.Ok,
data: [...api.responses.orders, {
_id: '000000000000000000000000',
token_id_in: '1d7f33bd23d85e1a25d87d86fac4f199c3197a2f7afeb662a0f34e1e776f726c646d6f62696c65746f6b656e',
token_id_out: '000000000000000000000000000000000000000000000000000000006c6f76656c616365',
dex: 'MUESLISWAP',
status: 'COMPLETE',
user_address: 'addr1q9qhyvkm5fytm5ckgshny0zz08a3urhhh7ckdqxcm27av40eafn3v5lr2w2n2er9uj7c743mt42gpe8tgek6394z9t7qn4yjzl',
user_stake: 'stake1u8u75eck203489f4v3j7f0v02ca464yqun45vmdgj63z4lqm9pu9k',
amount_in: 15.330409,
expected_out_amount: 3.756354,
actual_out_amount: 5.801912,
is_dexhunter: false,
submission_time: undefined,
last_update: undefined,
tx_hash: 'ldlldpldlpelpflepflepflpelfpelfpleplfpelfpelfplepflpelfpelfpelfp',
output_index: 0,
update_tx_hash: 'a8b77336d8600f1c8dac0ed90d0ab9c4f1e815bb25f4e168aaaadd130f81457d',
is_stop_loss: false,
is_oor: false,
batcher_fee: 1.15,
deposit: 1
}, {
_id: '111111111111111111111111',
token_id_in: '1d7f33bd23d85e1a25d87d86fac4f199c3197a2f7afeb662a0f34e1e776f726c646d6f62696c65746f6b656e',
token_id_out: '000000000000000000000000000000000000000000000000000000006c6f76656c616365',
dex: 'MUESLISWAP',
status: 'COMPLETE',
user_address: 'addr1q9qhyvkm5fytm5ckgshny0zz08a3urhhh7ckdqxcm27av40eafn3v5lr2w2n2er9uj7c743mt42gpe8tgek6394z9t7qn4yjzl',
user_stake: 'stake1u8u75eck203489f4v3j7f0v02ca464yqun45vmdgj63z4lqm9pu9k',
amount_in: 15.330409,
expected_out_amount: 3.756354,
actual_out_amount: 5.801912,
is_dexhunter: false,
submission_time: undefined,
last_update: undefined,
tx_hash: 'kskskkskskskskkskskskkskskkskskskkskskskkskskkskskskkskskskskksk',
output_index: 0,
update_tx_hash: 'a8b77336d8600f1c8dac0ed90d0ab9c4f1e815bb25f4e168aaaadd130f81457d',
is_stop_loss: false,
is_oor: false,
batcher_fee: 1.15,
deposit: 1
}]
}
});
const dhApi = dexhunterApiMaker(config);
const result = await dhApi.orders();
expect(mockFetchData).toHaveBeenCalledWith({
method: 'get',
url: 'https://api-us.dexhunterv3.app/swap/orders/someAddress',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-Partner-Id': 'somePartnerId'
}
});
expect(result.tag).toBe('right');
});
it('should return a left if Dexhunter fails', async () => {
mockFetchData.mockResolvedValueOnce({
tag: 'left',
error: {
status: 404,
message: 'Not found',
responseData: {
detail: 'No orders for this address'
}
}
});
const dhApi = dexhunterApiMaker(config);
const result = await dhApi.orders();
if (result.tag !== 'left') fail();
expect(result.tag).toBe('left');
expect(result.error.message).toContain('No orders for this address');
});
});
describe('estimate()', () => {
it('calls /swap/estimate if neither wantedPrice nor amountOut are given', async () => {
mockFetchData.mockResolvedValueOnce({
tag: 'right',
value: {
status: 200,
data: api.responses.estimate
}
});
const dhApi = dexhunterApiMaker(config);
const result = await dhApi.estimate(api.inputs.estimate);
expect(mockFetchData).toHaveBeenCalledWith({
method: 'post',
url: 'https://api-us.dexhunterv3.app/swap/estimate',
headers: expect.objectContaining({
'X-Partner-Id': 'somePartnerId'
}),
data: expect.any(Object)
});
expect(result.tag).toBe('right');
});
it('calls /swap/reverseEstimate if amountOut is provided', async () => {
mockFetchData.mockResolvedValueOnce({
tag: 'right',
value: {
status: 200,
data: api.responses.reverseEstimate
}
});
const dhApi = dexhunterApiMaker(config);
const result = await dhApi.estimate(api.inputs.reverseEstimate);
expect(mockFetchData).toHaveBeenCalledWith(expect.objectContaining({
url: 'https://api-us.dexhunterv3.app/swap/reverseEstimate',
method: 'post'
}));
expect(result.tag).toBe('right');
});
it('calls /swap/limit/estimate if wantedPrice is provided', async () => {
mockFetchData.mockResolvedValueOnce({
tag: 'right',
value: {
status: 200,
data: {
...api.responses.estimate,
wantedPrice: 1
}
}
});
const dhApi = dexhunterApiMaker(config);
const result = await dhApi.estimate({
...api.inputs.estimate,
wantedPrice: 1
});
expect(mockFetchData).toHaveBeenCalledWith(expect.objectContaining({
data: {
amount_in: 10,
blacklisted_dexes: ['WINGRIDER'],
dex: 'MINSWAP',
multiples: 1,
token_in: '',
token_out: 'af2e27f580f7f08e93190a81f72462f153026d06450924726645891b44524950',
wanted_price: 1
},
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-Partner-Id': 'somePartnerId'
},
method: 'post',
url: 'https://api-us.dexhunterv3.app/swap/limit/estimate'
}));
expect(result.tag).toBe('right');
});
it('should return a left if Dexhunter fails (server error)', async () => {
mockFetchData.mockResolvedValueOnce({
tag: 'left',
error: {
status: 400,
message: 'Bad request',
responseData: {
detail: 'estimate error'
}
}
});
const dhApi = dexhunterApiMaker(config);
const result = await dhApi.estimate(api.inputs.estimate);
if (result.tag !== 'left') fail();
expect(result.tag).toBe('left');
expect(result.error.message).toContain('estimate error');
});
});
describe('create()', () => {
it('calls /swap/build if wantedPrice is not provided', async () => {
mockFetchData.mockResolvedValueOnce({
tag: 'right',
value: {
status: 200,
data: {}
}
});
const dhApi = dexhunterApiMaker(config);
const result = await dhApi.create(api.inputs.create[0]);
expect(mockFetchData).toHaveBeenCalledWith({
method: 'post',
url: 'https://api-us.dexhunterv3.app/swap/build',
headers: expect.objectContaining({
'X-Partner-Id': 'somePartnerId'
}),
data: expect.any(Object)
});
expect(result.tag).toBe('right');
});
it('calls /swap/limit/build if wantedPrice is provided', async () => {
mockFetchData.mockResolvedValueOnce({
tag: 'right',
value: {
status: 200,
data: {}
}
});
const dhApi = dexhunterApiMaker(config);
const result = await dhApi.create(api.inputs.create[2]);
expect(mockFetchData).toHaveBeenCalledWith(expect.objectContaining({
url: 'https://api-us.dexhunterv3.app/swap/limit/build'
}));
expect(result.tag).toBe('right');
});
it('should return a left if Dexhunter fails', async () => {
mockFetchData.mockResolvedValueOnce({
tag: 'left',
error: {
status: 500,
message: 'Create error',
responseData: {
detail: 'could not build swap'
}
}
});
const dhApi = dexhunterApiMaker(config);
const result = await dhApi.create(api.inputs.create[0]);
if (result.tag !== 'left') fail();
expect(result.tag).toBe('left');
expect(result.error.message).toContain('could not build swap');
});
});
describe('cancel()', () => {
it('calls /swap/cancel successfully', async () => {
mockFetchData.mockResolvedValueOnce({
tag: 'right',
value: {
status: 200,
data: api.responses.cancel
}
});
const dhApi = dexhunterApiMaker(config);
const result = await dhApi.cancel(api.inputs.cancel);
expect(mockFetchData).toHaveBeenCalledWith({
method: 'post',
url: 'https://api-us.dexhunterv3.app/swap/cancel',
headers: expect.objectContaining({
'X-Partner-Id': 'somePartnerId'
}),
data: expect.any(Object)
});
expect(result.tag).toBe('right');
});
it('should return a left if Dexhunter fails', async () => {
mockFetchData.mockResolvedValueOnce({
tag: 'left',
error: {
status: 500,
message: 'Cancel error',
responseData: {
detail: 'could not cancel'
}
}
});
const dhApi = dexhunterApiMaker(config);
const result = await dhApi.cancel(api.inputs.cancel);
if (result.tag !== 'left') fail();
expect(result.tag).toBe('left');
expect(result.error.message).toContain('could not cancel');
});
});
});
//# sourceMappingURL=api-maker.test.js.map