UNPKG

@yoroi/swap

Version:
442 lines (440 loc) 14 kB
"use strict"; import { isLeft, isRight } from '@yoroi/common'; import { Chain, Portfolio } from '@yoroi/types'; import { steelswapApiMaker } from './api-maker'; const mockConfig = { address: 'addr1test', network: Chain.Network.Mainnet, primaryTokenInfo: { id: '.', name: 'Cardano', ticker: 'ADA', decimals: 6, logo: null, description: '', website: '', policyId: '', fingerprint: '', group: 'ADA', kind: 'ft', image: null, icon: null, symbol: 'ADA', metadatas: {}, isPrimaryToken: true, status: Portfolio.Token.Status.Valid, application: Portfolio.Token.Application.General, tag: '', reference: '', originalImage: '', nature: Portfolio.Token.Nature.Primary, type: Portfolio.Token.Type.FT }, isPrimaryToken: token => token === '.', request: jest.fn() }; describe('steelswapApiMaker', () => { const mockTokensResponse = [{ ticker: 'USDA', name: 'USDA', policyId: 'fe7c786ab321f41c654ef6c1af7b3250a613c24e4213e0425a7ae456', policyName: '55534441', decimals: 6 }]; const mockTokensApiResponse = { tag: 'right', value: { status: 200, data: mockTokensResponse } }; beforeEach(() => { jest.clearAllMocks(); }); it('should return proxy for non-mainnet networks', async () => { const config = { ...mockConfig, network: Chain.Network.Preprod }; const api = steelswapApiMaker(config); const result = await api.tokens(); expect(isLeft(result)).toBe(true); if (isLeft(result)) { expect(result.error).toMatchObject({ status: -3, message: 'Steelswap api only works on mainnet' }); } }); it('should create API instance for mainnet', () => { const api = steelswapApiMaker(mockConfig); expect(api).toBeDefined(); expect(typeof api.tokens).toBe('function'); expect(typeof api.orders).toBe('function'); expect(typeof api.estimate).toBe('function'); expect(typeof api.create).toBe('function'); expect(typeof api.cancel).toBe('function'); }); describe('tokens', () => { it('should handle tokens request successfully', async () => { const mockResponse = [{ ticker: 'USDA', name: 'USDA', policyId: 'fe7c786ab321f41c654ef6c1af7b3250a613c24e4213e0425a7ae456', policyName: '55534441', decimals: 6 }]; mockConfig.request = jest.fn().mockResolvedValue({ tag: 'right', value: { status: 200, data: mockResponse } }); const api = steelswapApiMaker(mockConfig); const result = await api.tokens(); expect(isRight(result)).toBe(true); if (isRight(result)) { expect(result.value.data).toHaveLength(1); expect(result.value.data[0]).toMatchObject({ ticker: 'USDA', decimals: 6 }); } }); it('should handle tokens request error', async () => { mockConfig.request = jest.fn().mockResolvedValue({ tag: 'left', error: { status: 500, message: 'Internal Server Error', responseData: null } }); const api = steelswapApiMaker(mockConfig); const result = await api.tokens(); expect(isLeft(result)).toBe(true); }); }); describe('orders', () => { it('should handle orders request successfully', async () => { const mockResponse = { orders: [{ source: 'MuesliSwap', firstObserved: 1762266077000, lastUpdated: 1762266077000, totalSubmitted: { lovelace: 13200000 }, totalRequested: { fe7c786ab321f41c654ef6c1af7b3250a613c24e4213e0425a7ae45655534441: 15000000 }, totalReceived: {}, swaps: [{ dex: 'MinswapV2', orderType: 0, txStatus: 'queued', submitTxHash: '0788552a7f0bfe547d47be51e36f579cda23b4adfccefeb7f74ea90e395c8bd2', submitTxIndex: 0, submitTime: 1762266077000, submitAssets: [{ lovelace: 13200000 }], requestAssets: [{ fe7c786ab321f41c654ef6c1af7b3250a613c24e4213e0425a7ae45655534441: 15000000 }], executeTxHash: null, executeTxIndex: null, executeTime: null, receivedAssets: null }] }], page: 0, lastPage: 0 }; mockConfig.request = jest.fn().mockResolvedValue({ tag: 'right', value: { status: 200, data: mockResponse } }); const api = steelswapApiMaker(mockConfig); const result = await api.orders(); expect(isRight(result)).toBe(true); if (isRight(result)) { expect(result.value.data).toHaveLength(1); expect(result.value.data[0]).toMatchObject({ txHash: '0788552a7f0bfe547d47be51e36f579cda23b4adfccefeb7f74ea90e395c8bd2', status: 'open' }); } }); }); describe('estimate', () => { it('should handle estimate request successfully', async () => { const mockResponse = { tokenA: 'lovelace', quantityA: 2000000, // Already in decimal format (isFloat=true) tokenB: 'fe7c786ab321f41c654ef6c1af7b3250a613c24e4213e0425a7ae45655534441', quantityB: 1089627, // Already in decimal format (isFloat=true) totalFee: 0.1, // Already in ADA (isFloat=true) totalDeposit: 0, steelswapFee: 0, bonusOut: 0, price: 1.9311019603443615, splitGroup: [[{ tokenA: 'lovelace', quantityA: 2000000, // Already in decimal format (isFloat=true) tokenB: 'fe7c786ab321f41c654ef6c1af7b3250a613c24e4213e0425a7ae45655534441', quantityB: 1089627, // Already in decimal format (isFloat=true) totalFee: 0.1, // Already in ADA (isFloat=true) totalDeposit: 0, steelswapFee: 0, bonusOut: 0, price: 1.9311019603443615, pools: [{ dex: 'Splash', poolId: 'test-pool-id', quantityA: 2000000, // Already in decimal format (isFloat=true) quantityB: 1089627, // Already in decimal format (isFloat=true) batcherFee: 0.1, // Already in ADA (isFloat=true) deposit: 0, volumeFee: 1000 // Still in lovelace, will be converted }] }]] }; mockConfig.request = jest.fn().mockResolvedValue({ tag: 'right', value: { status: 200, data: mockResponse } }); const api = steelswapApiMaker(mockConfig); const result = await api.estimate({ tokenIn: '.', tokenOut: 'fe7c786ab321f41c654ef6c1af7b3250a613c24e4213e0425a7ae456.55534441', amountIn: 2, // 2 ADA in decimal slippage: 0.5 }); expect(isRight(result)).toBe(true); if (isRight(result)) { expect(result.value.data.totalInput).toBe(2000000); // Already in decimal format (isFloat=true) expect(result.value.data.totalOutput).toBe(1089627); // Already in decimal format (isFloat=true) } }); it('should handle estimate error', async () => { mockConfig.request = jest.fn().mockResolvedValueOnce(mockTokensApiResponse).mockResolvedValueOnce({ tag: 'left', error: { status: 422, message: 'Validation Error', responseData: { detail: 'Invalid token pair' } } }); const api = steelswapApiMaker(mockConfig); const result = await api.estimate({ tokenIn: '.', tokenOut: 'invalid.invalid', amountIn: 1000000, slippage: 0.5 }); expect(isLeft(result)).toBe(true); }); it('should reject limit estimates (wantedPrice)', async () => { const api = steelswapApiMaker(mockConfig); const result = await api.estimate({ tokenIn: '.', tokenOut: 'fe7c786ab321f41c654ef6c1af7b3250a613c24e4213e0425a7ae456.55534441', amountIn: 2, slippage: 0.5, wantedPrice: 1.5 // Limit estimate - should be rejected }); expect(isLeft(result)).toBe(true); if (isLeft(result)) { expect(result.error.message).toBe('Steelswap Aggregator only supports market'); expect(result.error.status).toBe(-1); } }); it('should handle estimate response with invalid structure', async () => { const mockResponse = { splitGroup: [[]] }; mockConfig.request = jest.fn().mockResolvedValueOnce(mockTokensApiResponse).mockResolvedValueOnce({ tag: 'right', value: { status: 200, data: mockResponse } }); const api = steelswapApiMaker(mockConfig); const result = await api.estimate({ tokenIn: '.', tokenOut: '.', amountIn: 1000000, slippage: 0.5 }); expect(isLeft(result)).toBe(true); if (isLeft(result)) { expect(result.error.message).toContain('No liquidity pools'); } }); }); describe('create', () => { it('should handle create request successfully', async () => { const buildResponse = { tx: 'test-cbor-hex', p: false }; const estimateResponse = { tokenA: 'lovelace', quantityA: 2000000, // Already in decimal format (isFloat=true) tokenB: 'fe7c786ab321f41c654ef6c1af7b3250a613c24e4213e0425a7ae45655534441', quantityB: 1089627, // Already in decimal format (isFloat=true) totalFee: 0.1, // Already in ADA (isFloat=true) totalDeposit: 0, steelswapFee: 0, bonusOut: 0, price: 1.9311019603443615, splitGroup: [[{ tokenA: 'lovelace', quantityA: 2000000, // Already in decimal format (isFloat=true) tokenB: 'fe7c786ab321f41c654ef6c1af7b3250a613c24e4213e0425a7ae45655534441', quantityB: 1089627, // Already in decimal format (isFloat=true) totalFee: 0.1, // Already in ADA (isFloat=true) totalDeposit: 0, steelswapFee: 0, bonusOut: 0, price: 1.9311019603443615, pools: [{ dex: 'Splash', poolId: 'test-pool-id', quantityA: 2000000, // Already in decimal format (isFloat=true) quantityB: 1089627, // Already in decimal format (isFloat=true) batcherFee: 0.1, // Already in ADA (isFloat=true) deposit: 0, volumeFee: 1000 // Still in lovelace, will be converted }] }]] }; // Mock build request and estimate request (called internally) mockConfig.request = jest.fn().mockResolvedValueOnce({ tag: 'right', value: { status: 200, data: buildResponse } }).mockResolvedValueOnce({ tag: 'right', value: { status: 200, data: estimateResponse } }); const api = steelswapApiMaker(mockConfig); const result = await api.create({ tokenIn: '.', tokenOut: 'fe7c786ab321f41c654ef6c1af7b3250a613c24e4213e0425a7ae456.55534441', amountIn: 2, // 2 ADA in decimal slippage: 0.5, inputs: ['test-utxo'] }); expect(isRight(result)).toBe(true); if (isRight(result)) { expect(result.value.data.cbor).toBe('test-cbor-hex'); expect(result.value.data.totalInput).toBe(2000000); // Already in decimal format (isFloat=true) expect(result.value.data.totalOutput).toBe(1089627); // Already in decimal format (isFloat=true) expect(result.value.data.totalFee).toBe(0.1); // 0.1 (batcher) + 0 (steelswap) = 0.1 ADA (already converted) } }); it('should handle create request when estimate fails', async () => { const buildResponse = { tx: 'test-cbor-hex', p: false }; mockConfig.request = jest.fn().mockResolvedValueOnce({ tag: 'right', value: { status: 200, data: buildResponse } }).mockResolvedValueOnce({ tag: 'left', error: { status: 500, message: 'Estimate failed', responseData: null } }); const api = steelswapApiMaker(mockConfig); const result = await api.create({ tokenIn: '.', tokenOut: '.', amountIn: 2000000, slippage: 0.5, inputs: [] }); expect(isRight(result)).toBe(true); if (isRight(result)) { expect(result.value.data.cbor).toBe('test-cbor-hex'); // Should have minimal data when estimate fails expect(result.value.data.totalInput).toBe(0); } }); }); describe('cancel', () => { it('should handle cancel request successfully', async () => { const mockResponse = 'test-cancel-cbor-hex'; mockConfig.request = jest.fn().mockResolvedValue({ tag: 'right', value: { status: 200, data: mockResponse } }); const api = steelswapApiMaker(mockConfig); const result = await api.cancel({ order: { aggregator: 'steelswap', protocol: 'minswap-v2', status: 'open', tokenIn: '.', tokenOut: '.', amountIn: 1000000, expectedAmountOut: 2000000, actualAmountOut: 2000000, txHash: 'test-tx-hash', outputIndex: 0 } }); expect(isRight(result)).toBe(true); if (isRight(result)) { expect(result.value.data.cbor).toBe('test-cancel-cbor-hex'); } }); }); }); //# sourceMappingURL=api-maker.test.js.map