UNPKG

@yoroi/swap

Version:
445 lines (444 loc) 14.8 kB
"use strict"; import { Chain, Portfolio, Swap } from '@yoroi/types'; import { transformersMaker } from './transformers'; 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 === '.', partner: 'yoroi-aggregator' }; describe('transformersMaker', () => { const transformers = transformersMaker(mockConfig); describe('tokens', () => { it('should transform tokens response correctly', () => { const mockResponse = [{ ticker: 'ADA', name: 'Cardano', policyId: 'lovelace', policyName: '', decimals: 6 }, { ticker: 'USDA', name: 'USDA', policyId: 'fe7c786ab321f41c654ef6c1af7b3250a613c24e4213e0425a7ae456', policyName: '55534441', decimals: 6 }]; const result = transformers.tokens.response(mockResponse); expect(result).toHaveLength(1); // Primary token is filtered out expect(result[0]).toMatchObject({ id: 'fe7c786ab321f41c654ef6c1af7b3250a613c24e4213e0425a7ae456.55534441', ticker: 'USDA', name: 'USDA', decimals: 6, status: Portfolio.Token.Status.Valid }); }); it('should filter out tokens with null decimals', () => { const mockResponse = [{ ticker: 'TEST', name: 'Test Token', policyId: 'test123', policyName: 'TEST', decimals: null }]; const result = transformers.tokens.response(mockResponse); expect(result).toHaveLength(0); }); }); describe('orders', () => { it('should transform orders response correctly', () => { 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: 1, lastPage: 1 }; const result = transformers.orders.response(mockResponse); expect(result).toHaveLength(1); expect(result[0]).toMatchObject({ status: 'open', txHash: '0788552a7f0bfe547d47be51e36f579cda23b4adfccefeb7f74ea90e395c8bd2', outputIndex: 0, tokenIn: '.', tokenOut: 'fe7c786ab321f41c654ef6c1af7b3250a613c24e4213e0425a7ae456.55534441', amountIn: 13200000, // Already in decimal format (isFloat=true) expectedAmountOut: 15000000, // Already in decimal format (isFloat=true) actualAmountOut: 15000000, // Already in decimal format (isFloat=true) aggregator: Swap.Aggregator.Steelswap, protocol: Swap.Protocol.Minswap_v2 }); }); it('should map txStatus correctly', () => { const statusMap = { txPending: 'open', queued: 'open', executed: 'matched', cancelled: 'canceled' }; Object.entries(statusMap).forEach(([txStatus, expectedStatus]) => { const mockResponse = { orders: [{ source: 'Test', firstObserved: 0, lastUpdated: 0, totalSubmitted: {}, totalRequested: {}, totalReceived: {}, swaps: [{ dex: 'Minswap', orderType: 0, txStatus: txStatus, submitTxHash: 'test', submitTxIndex: 0, submitTime: 0, submitAssets: [{ lovelace: 1000000 }], requestAssets: [{ lovelace: 2000000 }], executeTxHash: null, executeTxIndex: null, executeTime: null, receivedAssets: null }] }], page: 1, lastPage: 1 }; const result = transformers.orders.response(mockResponse); expect(result[0]?.status).toBe(expectedStatus); }); }); }); describe('estimate', () => { it('should transform estimate request correctly', () => { const request = transformers.estimate.request({ tokenIn: '.', tokenOut: 'fe7c786ab321f41c654ef6c1af7b3250a613c24e4213e0425a7ae456.55534441', amountIn: 2, // 2 ADA in decimal slippage: 0.5, blockedProtocols: [Swap.Protocol.Minswap_v1] }); expect(request).toMatchObject({ tokenA: 'lovelace', tokenB: 'fe7c786ab321f41c654ef6c1af7b3250a613c24e4213e0425a7ae45655534441', quantity: 2, // Already in decimal format (isFloat=true) predictFromOutputAmount: false, partner: 'yoroi-aggregator', isFloat: true }); }); it('should handle amountOut instead of amountIn', () => { const request = transformers.estimate.request({ tokenIn: '.', tokenOut: 'fe7c786ab321f41c654ef6c1af7b3250a613c24e4213e0425a7ae456.55534441', amountOut: 1, // 1 token in decimal slippage: 0.5 }); expect(request).toMatchObject({ quantity: 1, // Already in decimal format (isFloat=true) predictFromOutputAmount: true, isFloat: true }); }); it('should transform estimate response correctly (SplitOutput)', () => { 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, 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 // Already in decimal format (isFloat=true) }] }; const result = transformers.estimate.response(mockResponse); expect(result).toMatchObject({ totalInput: 2000000, // Already in decimal format (isFloat=true) totalOutput: 1089627, // Already in decimal format (isFloat=true) totalFee: 0.1, // 100000 (batcher) + 0 (steelswap) = 0.1 ADA (already converted) batcherFee: 0.1, // Already in ADA (isFloat=true) deposits: 0, aggregatorFee: 0, frontendFee: 0, netPrice: 0.5448135, // 1089627 / 2000000 = output/input priceImpact: 0, splits: [{ amountIn: 2000000, // Already in decimal format (isFloat=true) expectedOutput: 1089627, // Already in decimal format (isFloat=true) batcherFee: 0.1, // Already in ADA (isFloat=true) deposits: 0, fee: 1000, // Already in decimal format (isFloat=true) poolFee: 1000, // Already in decimal format (isFloat=true) protocol: Swap.Protocol.Splash_v1, aggregator: Swap.Aggregator.Steelswap, aggregatorDexKey: 'Splash', aggregatorPoolId: 'test-pool-id' }] }); }); it('should transform estimate response correctly (HopSplitOutput)', () => { 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 // Already in decimal format (isFloat=true) }] }]] }; const result = transformers.estimate.response(mockResponse); expect(result).toMatchObject({ totalInput: 2000000, // Already in decimal format (isFloat=true) totalOutput: 1089627, // Already in decimal format (isFloat=true) totalFee: 0.1 // 100000 (batcher) + 0 (steelswap) = 0.1 ADA (already converted) }); }); it('should throw error for invalid estimate response', () => { const mockResponse = { splitGroup: [[]] }; expect(() => { transformers.estimate.response(mockResponse); }).toThrow('Invalid estimate response'); }); }); describe('create', () => { it('should transform create request correctly', () => { const request = transformers.create.request({ tokenIn: '.', tokenOut: 'fe7c786ab321f41c654ef6c1af7b3250a613c24e4213e0425a7ae456.55534441', amountIn: 2, // 2 ADA in decimal slippage: 0.5, inputs: ['test-utxo-1', 'test-utxo-2'], blockedProtocols: [Swap.Protocol.Minswap_v1] }); expect(request).toMatchObject({ tokenA: 'lovelace', tokenB: 'fe7c786ab321f41c654ef6c1af7b3250a613c24e4213e0425a7ae45655534441', quantity: 2, // Already in decimal format (isFloat=true) address: 'addr1test', utxos: ['test-utxo-1', 'test-utxo-2'], slippage: 50, // 0.5% = 50 basis points partner: 'yoroi-aggregator', isFloat: true }); }); it('should handle zero slippage', () => { const request = transformers.create.request({ tokenIn: '.', tokenOut: 'fe7c786ab321f41c654ef6c1af7b3250a613c24e4213e0425a7ae456.55534441', amountIn: 2000000, inputs: [] }); expect(request.slippage).toBe(0); }); it('should transform create response correctly', () => { const mockResponse = { tx: 'test-cbor-hex', p: false }; const result = transformers.create.response(mockResponse); expect(result).toMatchObject({ aggregator: Swap.Aggregator.Steelswap, cbor: 'test-cbor-hex', splits: [], batcherFee: 0, deposits: 0, aggregatorFee: 0, frontendFee: 0, netPrice: 0, priceImpact: 0, totalFee: 0, totalInput: 0, totalOutput: 0, totalOutputWithoutSlippage: 0 }); }); }); describe('cancel', () => { it('should transform cancel request correctly', () => { const request = transformers.cancel.request({ order: { aggregator: Swap.Aggregator.Steelswap, protocol: Swap.Protocol.Minswap_v1, status: 'open', tokenIn: '.', tokenOut: 'fe7c786ab321f41c654ef6c1af7b3250a613c24e4213e0425a7ae456.55534441', amountIn: 1000000, expectedAmountOut: 2000000, actualAmountOut: 2000000, txHash: 'test-tx-hash', outputIndex: 0 } }); expect(request).toMatchObject({ txList: [{ txHash: 'test-tx-hash', txIndex: 0 }], ttl: 900, partner: 'yoroi-aggregator' }); }); it('should transform cancel response correctly', () => { const mockResponse = 'test-cancel-cbor-hex'; const result = transformers.cancel.response(mockResponse); expect(result).toEqual({ cbor: 'test-cancel-cbor-hex' }); }); }); describe('token ID conversion', () => { it('should convert lovelace to primary token ID', () => { const transformers = transformersMaker(mockConfig); // Test through estimate request const request = transformers.estimate.request({ tokenIn: '.', tokenOut: '.', amountIn: 1, // Already in decimal format (isFloat=true) slippage: 0.5 }); expect(request.tokenA).toBe('lovelace'); expect(request.tokenB).toBe('lovelace'); }); it('should convert hex token ID to portfolio format', () => { const hexTokenId = 'fe7c786ab321f41c654ef6c1af7b3250a613c24e4213e0425a7ae45655534441'; const expectedPortfolioId = 'fe7c786ab321f41c654ef6c1af7b3250a613c24e4213e0425a7ae456.55534441'; const request = transformers.estimate.request({ tokenIn: expectedPortfolioId, tokenOut: '.', amountIn: 1, // Already in decimal format (isFloat=true) slippage: 0.5 }); expect(request.tokenA).toBe(hexTokenId); }); }); }); //# sourceMappingURL=transformers.test.js.map