UNPKG

@yoroi/swap

Version:
339 lines (329 loc) 12.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.transformersMaker = exports.toSwapProtocol = exports.fromSwapProtocol = exports.SteelswapProtocols = void 0; var _types = require("@yoroi/types"); var _types2 = require("./types"); const transformersMaker = ({ primaryTokenInfo, address, isPrimaryToken, partner }) => { const fromTokenId = tokenId => { if (tokenId === 'lovelace' || tokenId === 'ADA') { return primaryTokenInfo.id; } // If already in format policyId.hexName, return as is if (tokenId.includes('.')) { return tokenId; } // Steelswap uses hex-encoded format: policyId + hexName (no separator) // PolicyId is always 56 characters, rest is hexName return `${tokenId.slice(0, 56)}.${tokenId.slice(56)}`; }; const toTokenId = tokenId => { if (isPrimaryToken(tokenId)) { return 'lovelace'; } return tokenId.replace('.', ''); }; const parseAssets = assets => { const result = []; for (const asset of assets) { for (const [token, amount] of Object.entries(asset)) { // Amounts are already in decimal format (isFloat=true) result.push({ token, amount }); } } return result; }; return { tokens: { response: res => res.map(({ ticker, name, policyId, policyName, decimals }) => { const hexName = policyName || ''; const id = `${policyId}.${hexName}`; // Filter out primary token (it's already known) // Check if policyId is 'lovelace' or if the constructed id matches primaryTokenInfo.id const isPrimary = policyId === 'lovelace' || id === primaryTokenInfo.id; if (isPrimary) return null; if (decimals === null || decimals === undefined) return null; return { status: _types.Portfolio.Token.Status.Valid, id, ticker: ticker ?? '', name: name ?? ticker ?? '', type: _types.Portfolio.Token.Type.FT, nature: _types.Portfolio.Token.Nature.Secondary, application: _types.Portfolio.Token.Application.General, fingerprint: '', decimals, description: '', originalImage: '', symbol: '', reference: '', tag: '', website: '' }; }).filter(v => !!v) }, orders: { response: res => { const orders = []; for (const orderStatus of res.orders) { for (const swap of orderStatus.swaps) { const submitAssets = swap.submitAssets ? parseAssets(swap.submitAssets) : []; const requestAssets = swap.requestAssets ? parseAssets(swap.requestAssets) : []; const receivedAssets = swap.receivedAssets ? parseAssets(swap.receivedAssets) : []; // Find tokenIn and tokenOut from assets const tokenInAsset = submitAssets[0]; const tokenOutAsset = requestAssets[0]; const receivedAsset = receivedAssets[0]; if (!tokenInAsset || !tokenOutAsset) continue; const tokenIn = fromTokenId(tokenInAsset.token); const tokenOut = fromTokenId(tokenOutAsset.token); const amountIn = tokenInAsset.amount; const expectedAmountOut = tokenOutAsset.amount; const actualAmountOut = receivedAsset?.amount ?? expectedAmountOut; // Map txStatus to our status const statusMap = { txPending: 'open', queued: 'open', executed: 'matched', cancelled: 'canceled' }; const status = statusMap[swap.txStatus] ?? 'open'; orders.push({ status, txHash: swap.submitTxHash, outputIndex: swap.submitTxIndex, tokenIn, tokenOut, updateTxHash: swap.executeTxHash ?? swap.submitTxHash, placedAt: swap.submitTime ? swap.submitTime : undefined, lastUpdate: swap.executeTime ? swap.executeTime : swap.submitTime ? swap.submitTime : undefined, amountIn, actualAmountOut, expectedAmountOut, aggregator: _types.Swap.Aggregator.Steelswap, protocol: toSwapProtocol(swap.dex), customId: `${swap.submitTxHash}#${swap.submitTxIndex}` }); } } return orders; } }, estimate: { request: ({ tokenIn, tokenOut, amountIn, amountOut, slippage: _slippage }) => { const tokenAId = toTokenId(tokenIn); const tokenBId = toTokenId(tokenOut); // Amounts are already in decimal format adjusted by token decimals (isFloat=true) const quantity = amountIn ?? amountOut ?? 0; return { tokenA: tokenAId, tokenB: tokenBId, quantity: Math.round(quantity), predictFromOutputAmount: amountOut !== undefined, ...(partner !== undefined && { partner }), isFloat: true }; }, response: data => { // Handle both SplitOutput and HopSplitOutput const isHopSplit = 'splitGroup' in data; // When isFloat=true, use top-level values for totals (they're in decimal format) // But extract pools from splitGroup if it's a HopSplitOutput const topLevelData = data; const splitOutput = isHopSplit ? data.splitGroup?.[0]?.[0] : data; if (!splitOutput || !splitOutput.pools || splitOutput.pools.length === 0) { throw new Error('Invalid estimate response: missing split output or pools'); } // When isFloat=true, all values are already in decimal format const splits = splitOutput.pools.map(pool => ({ amountIn: pool.quantityA, // Already in decimal format batcherFee: pool.batcherFee, // Already in ADA deposits: pool.deposit, // Already in ADA protocol: toSwapProtocol(pool.dex), expectedOutput: pool.quantityB, // Already in decimal format expectedOutputWithoutSlippage: pool.quantityB, // Already in decimal format fee: pool.volumeFee, // Already in decimal format (isFloat=true) initialPrice: pool.quantityA > 0 ? pool.quantityB / pool.quantityA : 0, finalPrice: pool.quantityA > 0 ? pool.quantityB / pool.quantityA : 0, poolFee: pool.volumeFee, // Already in decimal format (isFloat=true) poolId: pool.poolId, priceDistortion: 0, priceImpact: 0, aggregator: _types.Swap.Aggregator.Steelswap, aggregatorDexKey: pool.dex, aggregatorPoolId: pool.poolId })); // Use top-level values for totals (they're in decimal format when isFloat=true) const totalInput = topLevelData.quantityA; const totalOutput = topLevelData.quantityB; const deposits = topLevelData.totalDeposit; // Already in ADA const totalFee = topLevelData.totalFee + topLevelData.steelswapFee; const batcherFee = topLevelData.totalFee; // Already in ADA const aggregatorFee = topLevelData.steelswapFee; // Already in ADA const netPrice = totalInput > 0 ? totalOutput / totalInput : 0; return { splits, batcherFee, deposits, aggregatorFee, frontendFee: 0, netPrice, priceImpact: 0, totalFee, totalInput, totalOutput, totalOutputWithoutSlippage: totalOutput + topLevelData.bonusOut }; } }, create: { request: ({ tokenIn, tokenOut, amountIn, slippage, inputs }) => { const tokenAId = toTokenId(tokenIn); // AmountIn is already in decimal format adjusted by token decimals (isFloat=true) const quantity = amountIn; return { tokenA: tokenAId, tokenB: toTokenId(tokenOut), quantity: Math.round(quantity), ...(partner !== undefined && { partner }), address, utxos: inputs ?? [], slippage: slippage ? Math.round(slippage * 100) : 0, // Convert percentage to basis points isFloat: true }; }, response: ({ tx, p: _p }) => { return { aggregator: _types.Swap.Aggregator.Steelswap, cbor: tx, splits: [], batcherFee: 0, deposits: 0, aggregatorFee: 0, frontendFee: 0, netPrice: 0, priceImpact: 0, totalFee: 0, totalInput: 0, totalOutput: 0, totalOutputWithoutSlippage: 0 }; } }, cancel: { request: ({ order }) => ({ txList: [{ txHash: order.txHash, txIndex: order.outputIndex ?? 0 }], ttl: 900, ...(partner !== undefined && { partner }) }), response: txHex => ({ cbor: txHex }) } }; }; exports.transformersMaker = transformersMaker; const toSwapProtocol = dex => { const dexUpper = dex.toUpperCase(); const dexMap = { CSWAP: _types.Swap.Protocol.Cswap, GENIUSYIELD: _types.Swap.Protocol.Genius, MINSWAP: _types.Swap.Protocol.Minswap_v1, MINSWAPV2: _types.Swap.Protocol.Minswap_v2, MINSWAPV2ROUTER: _types.Swap.Protocol.Minswap_v2, MUESLISWAP: _types.Swap.Protocol.Muesliswap_v2, SPECTRUM: _types.Swap.Protocol.Spectrum_v1, SPLASH: _types.Swap.Protocol.Splash_v1, SPLASHROUTER: _types.Swap.Protocol.Splash_v1, SUNDAESWAP: _types.Swap.Protocol.Sundaeswap_v1, SUNDAESWAPV3: _types.Swap.Protocol.Sundaeswap_v3, VYFI: _types.Swap.Protocol.Vyfi_v1, WINGRIDERS: _types.Swap.Protocol.Wingriders_v1, WINGRIDERSV2: _types.Swap.Protocol.Wingriders_v2 }; return dexMap[dexUpper] ?? _types.Swap.Protocol.Unsupported; }; exports.toSwapProtocol = toSwapProtocol; const fromSwapProtocol = dex => { const protocolMap = { [_types.Swap.Protocol.Cswap]: _types2.Dex.CSWAP, [_types.Swap.Protocol.Genius]: _types2.Dex.GeniusYield, [_types.Swap.Protocol.Minswap_v1]: _types2.Dex.Minswap, [_types.Swap.Protocol.Minswap_v2]: _types2.Dex.MinswapV2, [_types.Swap.Protocol.Minswap_stable]: _types2.Dex.MinswapV2, [_types.Swap.Protocol.Muesliswap]: _types2.Dex.MuesliSwap, [_types.Swap.Protocol.Muesliswap_v1]: _types2.Dex.MuesliSwap, [_types.Swap.Protocol.Muesliswap_v2]: _types2.Dex.MuesliSwap, [_types.Swap.Protocol.Muesliswap_clp]: _types2.Dex.MuesliSwap, [_types.Swap.Protocol.Muesliswap_orderbook]: _types2.Dex.MuesliSwap, [_types.Swap.Protocol.Spectrum_v1]: _types2.Dex.Spectrum, [_types.Swap.Protocol.Splash_v1]: _types2.Dex.Splash, [_types.Swap.Protocol.Splash_v4]: _types2.Dex.Splash, [_types.Swap.Protocol.Splash_v5]: _types2.Dex.Splash, [_types.Swap.Protocol.Splash_v6]: _types2.Dex.Splash, [_types.Swap.Protocol.Sundaeswap_v1]: _types2.Dex.SundaeSwap, [_types.Swap.Protocol.Sundaeswap_v3]: _types2.Dex.SundaeSwapV3, [_types.Swap.Protocol.Vyfi_v1]: _types2.Dex.VyFi, [_types.Swap.Protocol.Wingriders_v1]: _types2.Dex.WingRiders, [_types.Swap.Protocol.Wingriders_v2]: _types2.Dex.WingRidersV2, [_types.Swap.Protocol.Wingriders_stable]: _types2.Dex.WingRidersV2, [_types.Swap.Protocol.Teddy_v1]: _types2.Dex.Unsupported, [_types.Swap.Protocol.Snekfun]: _types2.Dex.Unsupported, [_types.Swap.Protocol.Chadswap]: _types2.Dex.Unsupported, [_types.Swap.Protocol.Cerra]: _types2.Dex.Unsupported, [_types.Swap.Protocol.Unsupported]: _types2.Dex.Unsupported }; return protocolMap[dex] ?? _types2.Dex.Unsupported; }; exports.fromSwapProtocol = fromSwapProtocol; const SteelswapProtocols = exports.SteelswapProtocols = Object.values(_types2.Dex).filter(p => p !== _types2.Dex.Unsupported).map(toSwapProtocol); //# sourceMappingURL=transformers.js.map