diff --git a/CHANGELOG.md b/CHANGELOG.md index 290e391..f8cf5d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ All notable changes to Dexter will be documented in this file. +## [UNRELEASED] +- Minswap v2 integration + ## [v5.2.0] - Add `withMinimumReceive(minReceive: bigint)` to SwapRequest diff --git a/README.md b/README.md index fa28d47..586af9d 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@

Customizable Typescript SDK for interacting with Cardano DEXs.

+ diff --git a/src/constants.ts b/src/constants.ts index e662070..01036a1 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -9,6 +9,8 @@ export enum DatumParameterKey { Action = 'Action', TokenPolicyId = 'TokenPolicyId', TokenAssetName = 'TokenAssetName', + ReserveA = 'ReserveA', + ReserveB = 'ReserveB', /** * Swap/wallet info. @@ -34,6 +36,8 @@ export enum DatumParameterKey { BatcherFee = 'BatcherFee', DepositFee = 'DepositFee', ScooperFee = 'ScooperFee', + BaseFee = 'BaseFee', + FeeSharingNumerator = 'FeeSharingNumerator', /** * LP info. diff --git a/src/definition-builder.ts b/src/definition-builder.ts index c0852c8..f31c379 100644 --- a/src/definition-builder.ts +++ b/src/definition-builder.ts @@ -109,6 +109,8 @@ export class DefinitionBuilder { * Recursively pull parameters from datum using definition template. */ private extractParameters(definedDefinition: DefinitionField, templateDefinition: DefinitionField, foundParameters: DatumParameters = {}): DatumParameters { + if (! templateDefinition) return foundParameters; + if (templateDefinition instanceof Function) { templateDefinition(definedDefinition, foundParameters); diff --git a/src/dex/definitions/minswap-v2/order.ts b/src/dex/definitions/minswap-v2/order.ts new file mode 100644 index 0000000..e7c89fc --- /dev/null +++ b/src/dex/definitions/minswap-v2/order.ts @@ -0,0 +1,130 @@ +import { DatumParameterKey } from '@app/constants'; + +/** + * https://github.com/minswap/minswap-dex-v2/blob/main/src/types/order.ts + */ +export default { + constructor: 0, + fields: [ + { + constructor: 0, + fields: [ + { + bytes: DatumParameterKey.SenderPubKeyHash, + } + ] + }, + { + constructor: 0, + fields: [ + { + constructor: 0, + fields: [ + { + bytes: DatumParameterKey.SenderPubKeyHash, + } + ] + }, + { + constructor: 0, + fields: [ + { + constructor: 0, + fields: [ + { + constructor: 0, + fields: [ + { + bytes: DatumParameterKey.SenderStakingKeyHash, + } + ] + } + ] + } + ] + } + ] + }, + { + constructor: 0, + fields: [] + }, + { + constructor: 0, + fields: [ + { + constructor: 0, + fields: [ + { + bytes: DatumParameterKey.SenderPubKeyHash, + } + ] + }, + { + constructor: 0, + fields: [ + { + constructor: 0, + fields: [ + { + constructor: 0, + fields: [ + { + bytes: DatumParameterKey.SenderPubKeyHash, + } + ] + } + ] + } + ] + } + ] + }, + { + constructor: 0, + fields: [] + }, + { + constructor: 0, + fields: [ + { + bytes: DatumParameterKey.LpTokenPolicyId + }, + { + bytes: DatumParameterKey.LpTokenAssetName + } + ] + }, + { + constructor: 0, + fields: [ + { + constructor: 1, + fields: [] + }, + { + constructor: 0, + fields: [ + { + int: DatumParameterKey.SwapInAmount + } + ] + }, + { + int: DatumParameterKey.MinReceive + }, + { + constructor: 0, + fields: [] + } + ] + }, + { + int: DatumParameterKey.BatcherFee + }, + { + constructor: 1, + fields: [] + } + ] +} \ No newline at end of file diff --git a/src/dex/definitions/minswap-v2/pool.ts b/src/dex/definitions/minswap-v2/pool.ts new file mode 100644 index 0000000..3d8cefd --- /dev/null +++ b/src/dex/definitions/minswap-v2/pool.ts @@ -0,0 +1,59 @@ +import { DatumParameterKey } from '@app/constants'; +import { DatumParameters, DefinitionField } from '@app/types'; + +/** + * https://github.com/minswap/minswap-dex-v2/blob/main/src/types/pool.ts + */ +export default { + constructor: 0, + fields: [ + { + constructor: 0, + fields: [ + (field: DefinitionField, parameters: DatumParameters, shouldExtract: boolean = true) => { + return; + }, + ] + }, + { + constructor: 0, + fields: [ + { + bytes: DatumParameterKey.PoolAssetAPolicyId + }, + { + bytes: DatumParameterKey.PoolAssetAAssetName + } + ] + }, + { + constructor: 0, + fields: [ + { + bytes: DatumParameterKey.PoolAssetBPolicyId + }, + { + bytes: DatumParameterKey.PoolAssetBAssetName + } + ] + }, + { + int: DatumParameterKey.TotalLpTokens + }, + { + int: DatumParameterKey.ReserveA + }, + { + int: DatumParameterKey.ReserveB + }, + { + int: DatumParameterKey.BaseFee + }, + { + int: DatumParameterKey.FeeSharingNumerator + }, + (field: DefinitionField, parameters: DatumParameters, shouldExtract: boolean = true) => { + return; + }, + ] +} \ No newline at end of file diff --git a/src/dex/definitions/minswap/order.ts b/src/dex/definitions/minswap/order.ts index 121b179..3f9c196 100644 --- a/src/dex/definitions/minswap/order.ts +++ b/src/dex/definitions/minswap/order.ts @@ -27,7 +27,7 @@ export default { constructor: 0, fields: [ { - bytes: DatumParameterKey.SenderStakingKeyHash, + bytes: DatumParameterKey.SenderPubKeyHash, } ] } diff --git a/src/dex/logo/minswapv2.png b/src/dex/logo/minswapv2.png new file mode 100644 index 0000000..f82dd67 Binary files /dev/null and b/src/dex/logo/minswapv2.png differ diff --git a/src/dex/minswap-v2.ts b/src/dex/minswap-v2.ts new file mode 100644 index 0000000..fcd7aee --- /dev/null +++ b/src/dex/minswap-v2.ts @@ -0,0 +1,278 @@ +import { LiquidityPool } from './models/liquidity-pool'; +import { BaseDataProvider } from '@providers/data/base-data-provider'; +import { Asset, Token } from './models/asset'; +import { BaseDex } from './base-dex'; +import { + AssetAddress, + AssetBalance, + DatumParameters, + DefinitionConstr, + DefinitionField, + PayToAddress, RequestConfig, + SpendUTxO, + SwapFee, + UTxO +} from '@app/types'; +import { DefinitionBuilder } from '@app/definition-builder'; +import { correspondingReserves, lucidUtils } from '@app/utils'; +import { AddressType, DatumParameterKey } from '@app/constants'; +import order from '@dex/definitions/minswap-v2/order'; +import { BaseApi } from '@dex/api/base-api'; +import pool from '@dex/definitions/minswap-v2/pool'; +import { AddressDetails, Script } from 'lucid-cardano'; + +export class MinswapV2 extends BaseDex { + + public static readonly identifier: string = 'MinswapV2'; + public readonly api: BaseApi; + + /** + * On-Chain constants. + */ + public readonly lpTokenPolicyId: string = 'f5808c2c990d86da54bfc97d89cee6efa20cd8461616359478d96b4c'; + public readonly poolValidityAsset: string = 'f5808c2c990d86da54bfc97d89cee6efa20cd8461616359478d96b4c4d5350'; + public readonly orderScriptHash: string = 'c3e28c36c3447315ba5a56f33da6a6ddc1770a876a8d9f0cb3a97c4c'; + public readonly cancelDatum: string = 'd87a80'; + public readonly orderScript: Script = { + type: 'PlutusV2', + script: '590a600100003332323232323232323222222533300832323232533300c3370e900118058008991919299980799b87480000084cc004dd5980a180a980a980a980a980a980a98068030060a99980799b87480080084c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c94ccc080cdc3a4000002264646600200200e44a66604c00229404c8c94ccc094cdc78010028a51133004004001302a002375c60500026eb8c094c07800854ccc080cdc3a40040022646464646600200202844a66605000229404c8c94ccc09ccdd798161812981618129816181698128010028a51133004004001302c002302a0013374a9001198131ba90014bd701bae3026001301e002153330203370e900200089980900419ba548000cc090cdd2a400466048604a603c00497ae04bd70099981019b87375a6044604a66446464a66604866e1d200200114bd6f7b63009bab302930220023022001323300100100322533302700114c103d87a800013232323253330283371e00e004266e9520003302c374c00297ae0133006006003375660520066eb8c09c008c0ac008c0a4004c8cc004004030894ccc09400452f5bded8c0264646464a66604c66e3d22100002100313302a337606ea4008dd3000998030030019bab3027003375c604a0046052004604e0026eb8c094c07800920004a0944c078004c08c004c06c060c8c8c8c8c8c8c94ccc08ccdc3a40000022646464646464646464646464646464646464a6660706076004264646464646464649319299981e99b87480000044c8c94ccc108c1140084c92632375a60840046eb4c10000458c8cdd81822000982218228009bac3043001303b0091533303d3370e90010008a999820181d8048a4c2c2c607601064a66607866e1d2000001132323232323232325333047304a002132498c09401458cdc3a400460886ea8c120004c120008dd6982300098230011822000982200119b8748008c0f8dd51821000981d0060a99981e19b87480080044c8c8c8c8c8c94ccc114c1200084c926302300316375a608c002608c0046088002608800466e1d2002303e3754608400260740182a66607866e1d2004001132323232323232325333047304a002132498c09401458dd6982400098240011bad30460013046002304400130440023370e9001181f1baa3042001303a00c1533303c3370e9003000899191919191919192999823982500109924c604a00a2c66e1d200230443754609000260900046eb4c118004c118008c110004c110008cdc3a4004607c6ea8c108004c0e803054ccc0f0cdc3a40100022646464646464a66608a60900042649319299982199b87480000044c8c8c8c94ccc128c13400852616375a609600260960046eb4c124004c10401854ccc10ccdc3a4004002264646464a666094609a0042930b1bad304b001304b002375a6092002608200c2c608200a2c66e1d200230423754608c002608c0046eb4c110004c110008c108004c0e803054ccc0f0cdc3a401400226464646464646464a66608e60940042649318130038b19b8748008c110dd5182400098240011bad30460013046002375a60880026088004608400260740182a66607866e1d200c001132323232323232325333047304a002132498c09801458cdc3a400460886ea8c120004c120008dd6982300098230011822000982200119b8748008c0f8dd51821000981d0060a99981e19b87480380044c8c8c8c8c8c8c8c8c8c8c8c8c8c94ccc134c14000852616375a609c002609c0046eb4c130004c130008dd6982500098250011bad30480013048002375a608c002608c0046eb4c110004c110008cdc3a4004607c6ea8c108004c0e803054ccc0f0cdc3a4020002264646464646464646464a66609260980042649318140048b19b8748008c118dd5182500098250011bad30480013048002375a608c002608c0046eb4c110004c110008c108004c0e803054ccc0f0cdc3a40240022646464646464a66608a60900042646493181200219198008008031129998238008a4c2646600600660960046464a66608c66e1d2000001132323232533304d3050002132498c0b400c58cdc3a400460946ea8c138004c138008c130004c11000858c110004c12400458dd698230009823001182200098220011bac3042001303a00c1533303c3370e900a0008a99981f981d0060a4c2c2c6074016603a018603001a603001c602c01e602c02064a66606c66e1d200000113232533303b303e002149858dd7181e000981a0090a99981b19b87480080044c8c94ccc0ecc0f800852616375c607800260680242a66606c66e1d200400113232533303b303e002149858dd7181e000981a0090a99981b19b87480180044c8c94ccc0ecc0f800852616375c607800260680242c60680222c607200260720046eb4c0dc004c0dc008c0d4004c0d4008c0cc004c0cc008c0c4004c0c4008c0bc004c0bc008c0b4004c0b4008c0ac004c0ac008c0a4004c08407858c0840748c94ccc08ccdc3a40000022a66604c60420042930b0a99981199b87480080044c8c94ccc0a0c0ac00852616375c605200260420042a66604666e1d2004001132325333028302b002149858dd7181480098108010b1810800919299981119b87480000044c8c8c8c94ccc0a4c0b00084c8c9263253330283370e9000000899192999816981800109924c64a66605666e1d20000011323253330303033002132498c04400458c0c4004c0a400854ccc0accdc3a40040022646464646464a666068606e0042930b1bad30350013035002375a606600260660046eb4c0c4004c0a400858c0a400458c0b8004c09800c54ccc0a0cdc3a40040022a666056604c0062930b0b181300118050018b18150009815001181400098100010b1810000919299981099b87480000044c8c94ccc098c0a400852616375a604e002603e0042a66604266e1d20020011323253330263029002149858dd69813800980f8010b180f800919299981019b87480000044c8c94ccc094c0a000852616375a604c002603c0042a66604066e1d20020011323253330253028002149858dd69813000980f0010b180f000919299980f99b87480000044c8c8c8c94ccc098c0a400852616375c604e002604e0046eb8c094004c07400858c0740048c94ccc078cdc3a400000226464a666046604c0042930b1bae3024001301c0021533301e3370e900100089919299981198130010a4c2c6eb8c090004c07000858c070004dd618100009810000980f8011bab301d001301d001301c00237566034002603400260320026030002602e0046eb0c054004c0340184cc004dd5980a180a980a980a980a980a980a980680300591191980080080191299980a8008a50132323253330153375e00c00229444cc014014008c054008c064008c05c004c03001cc94ccc034cdc3a40000022a666020601600e2930b0a99980699b874800800454ccc040c02c01c526161533300d3370e90020008a99980818058038a4c2c2c601600c2c60200026020004601c002600c00229309b2b118029baa001230033754002ae6955ceaab9e5573eae815d0aba24c126d8799fd87a9f581c1eae96baf29e27682ea3f815aba361a0c6059d45e4bfbe95bbd2f44affff004c0126d8799fd87a9f581cc8b0cc61374d409ff9c8512317003e7196a3e4d48553398c656cc124ffff0001', + }; + + constructor(requestConfig: RequestConfig = {}) { + super(); + } + + public async liquidityPoolAddresses(provider: BaseDataProvider): Promise { + const validityAsset: Asset = Asset.fromIdentifier(this.poolValidityAsset); + const assetAddresses: AssetAddress[] = await provider.assetAddresses(validityAsset); + + return Promise.resolve([...new Set(assetAddresses.map((assetAddress: AssetAddress) => assetAddress.address))]); + } + + public async liquidityPools(provider: BaseDataProvider): Promise { + const validityAsset: Asset = Asset.fromIdentifier(this.poolValidityAsset); + const poolAddresses: string[] = await this.liquidityPoolAddresses(provider); + + const addressPromises: Promise[] = poolAddresses.map(async (address: string) => { + const utxos: UTxO[] = await provider.utxos(address, validityAsset); + + return await Promise.all( + utxos.map(async (utxo: UTxO) => { + return await this.liquidityPoolFromUtxo(provider, utxo); + }) + ) + .then((liquidityPools: (LiquidityPool | undefined)[]) => { + return liquidityPools.filter((liquidityPool?: LiquidityPool) => { + return liquidityPool !== undefined; + }) as LiquidityPool[] + }); + }); + + return Promise.all(addressPromises) + .then((liquidityPools: (Awaited)[]) => liquidityPools.flat()); + } + + public async liquidityPoolFromUtxo(provider: BaseDataProvider, utxo: UTxO): Promise { + if (! utxo.datumHash) { + return Promise.resolve(undefined); + } + + const relevantAssets: AssetBalance[] = utxo.assetBalances + .filter((assetBalance: AssetBalance) => { + const assetBalanceId: string = assetBalance.asset === 'lovelace' ? 'lovelace' : assetBalance.asset.identifier(); + + return assetBalanceId !== this.poolValidityAsset + && ! assetBalanceId.startsWith(this.lpTokenPolicyId); + }); + + // Irrelevant UTxO + if (relevantAssets.length < 2) { + return Promise.resolve(undefined); + } + + // Could be ADA/X or X/X pool + const assetAIndex: number = relevantAssets.length === 2 ? 0 : 1; + const assetBIndex: number = relevantAssets.length === 2 ? 1 : 2; + + const liquidityPool: LiquidityPool = new LiquidityPool( + MinswapV2.identifier, + relevantAssets[assetAIndex].asset, + relevantAssets[assetBIndex].asset, + relevantAssets[assetAIndex].quantity, + relevantAssets[assetBIndex].quantity, + utxo.address, + '', + '', + ); + + const lpTokenBalance: AssetBalance | undefined = utxo.assetBalances + .find((balance: AssetBalance) => { + return balance.asset !== 'lovelace' + && balance.asset.identifier() !== this.poolValidityAsset + && balance.asset.policyId === this.lpTokenPolicyId; + }); + + if (! lpTokenBalance || ! lpTokenBalance?.asset) return Promise.resolve(undefined); + + liquidityPool.lpToken = lpTokenBalance.asset as Asset; + liquidityPool.identifier = liquidityPool.lpToken.identifier(); + + try { + const builder: DefinitionBuilder = await (new DefinitionBuilder()) + .loadDefinition(pool); + const datum: DefinitionField = await provider.datumValue(utxo.datumHash); + const parameters: DatumParameters = builder.pullParameters(datum as DefinitionConstr); + + // Ignore Zap orders + if (typeof parameters.PoolAssetBPolicyId === 'string' && parameters.PoolAssetBPolicyId === this.lpTokenPolicyId) { + return undefined; + } + + liquidityPool.poolFeePercent = Number(parameters.BaseFee) / 100; + liquidityPool.totalLpTokens = typeof parameters.TotalLpTokens === 'number' + ? BigInt(parameters.TotalLpTokens) + : 0n; + } catch (e) { + return liquidityPool; + } + + return liquidityPool; + } + + estimatedGive(liquidityPool: LiquidityPool, swapOutToken: Token, swapOutAmount: bigint): bigint { + const poolFeeMultiplier: bigint = 10000n; + const poolFeeModifier: bigint = poolFeeMultiplier - BigInt(Math.round((liquidityPool.poolFeePercent / 100) * Number(poolFeeMultiplier))); + + const [reserveOut, reserveIn]: bigint[] = correspondingReserves(liquidityPool, swapOutToken); + + const swapInNumerator: bigint = swapOutAmount * reserveIn * poolFeeMultiplier; + const swapInDenominator: bigint = (reserveOut - swapOutAmount) * poolFeeModifier; + + return swapInNumerator / swapInDenominator + 1n; + } + + public estimatedReceive(liquidityPool: LiquidityPool, swapInToken: Token, swapInAmount: bigint): bigint { + const poolFeeMultiplier: bigint = 10000n; + const poolFeeModifier: bigint = poolFeeMultiplier - BigInt(Math.round((liquidityPool.poolFeePercent / 100) * Number(poolFeeMultiplier))); + + const [reserveIn, reserveOut]: bigint[] = correspondingReserves(liquidityPool, swapInToken); + + const swapOutNumerator: bigint = swapInAmount * reserveOut * poolFeeModifier; + const swapOutDenominator: bigint = swapInAmount * poolFeeModifier + reserveIn * poolFeeMultiplier; + + return swapOutNumerator / swapOutDenominator; + } + + public priceImpactPercent(liquidityPool: LiquidityPool, swapInToken: Token, swapInAmount: bigint): number { + const poolFeeMultiplier: bigint = 10000n; + const poolFeeModifier: bigint = poolFeeMultiplier - BigInt(Math.round((liquidityPool.poolFeePercent / 100) * Number(poolFeeMultiplier))); + + const [reserveIn, reserveOut]: bigint[] = correspondingReserves(liquidityPool, swapInToken); + + const swapOutNumerator: bigint = swapInAmount * poolFeeModifier * reserveOut; + const swapOutDenominator: bigint = swapInAmount * poolFeeModifier + reserveIn * poolFeeMultiplier; + + const priceImpactNumerator: bigint = (reserveOut * swapInAmount * swapOutDenominator * poolFeeModifier) + - (swapOutNumerator * reserveIn * poolFeeMultiplier); + const priceImpactDenominator: bigint = reserveOut * swapInAmount * swapOutDenominator * poolFeeMultiplier; + + return Number(priceImpactNumerator * 100n) / Number(priceImpactDenominator); + } + + public async buildSwapOrder(liquidityPool: LiquidityPool, swapParameters: DatumParameters, spendUtxos: SpendUTxO[] = []): Promise { + const batcherFee: SwapFee | undefined = this.swapOrderFees().find((fee: SwapFee) => fee.id === 'batcherFee'); + const deposit: SwapFee | undefined = this.swapOrderFees().find((fee: SwapFee) => fee.id === 'deposit'); + + if (! batcherFee || ! deposit) { + return Promise.reject('Parameters for datum are not set.'); + } + + swapParameters = { + ...swapParameters, + [DatumParameterKey.BatcherFee]: batcherFee.value, + [DatumParameterKey.LpTokenPolicyId]: liquidityPool.lpToken.policyId, + [DatumParameterKey.LpTokenAssetName]: liquidityPool.lpToken.nameHex, + }; + + const datumBuilder: DefinitionBuilder = new DefinitionBuilder(); + await datumBuilder.loadDefinition(order) + .then((builder: DefinitionBuilder) => { + builder.pushParameters(swapParameters); + }); + + return [ + this.buildSwapOrderPayment( + swapParameters, + { + address: lucidUtils.credentialToAddress( + { + type: 'Script', + hash: this.orderScriptHash, + }, + { + type: 'Key', + hash: swapParameters.SenderStakingKeyHash as string, + }, + ), + addressType: AddressType.Contract, + assetBalances: [ + { + asset: 'lovelace', + quantity: batcherFee.value + deposit.value, + }, + ], + datum: datumBuilder.getCbor(), + isInlineDatum: false, + spendUtxos: spendUtxos, + } + ) + ]; + } + + public async buildCancelSwapOrder(txOutputs: UTxO[], returnAddress: string): Promise { + const relevantUtxo: UTxO | undefined = txOutputs.find((utxo: UTxO) => { + const addressDetails: AddressDetails | undefined = lucidUtils.getAddressDetails(utxo.address); + + return (addressDetails.paymentCredential?.hash ?? '') === this.orderScriptHash; + }); + + if (! relevantUtxo) { + return Promise.reject('Unable to find relevant UTxO for cancelling the swap order.'); + } + + return [ + { + address: returnAddress, + addressType: AddressType.Base, + assetBalances: relevantUtxo.assetBalances, + isInlineDatum: false, + spendUtxos: [{ + utxo: relevantUtxo, + redeemer: this.cancelDatum, + validator: this.orderScript, + signer: returnAddress, + }], + } + ]; + } + + public swapOrderFees(): SwapFee[] { + return [ + { + id: 'batcherFee', + title: 'Batcher Fee', + description: 'Fee paid for the service of off-chain Laminar batcher to process transactions.', + value: 1_000000n, + isReturned: false, + }, + { + id: 'deposit', + title: 'Deposit', + description: 'This amount of ADA will be held as minimum UTxO ADA and will be returned when your order is processed or cancelled.', + value: 2_000000n, + isReturned: true, + }, + ]; + } + +} diff --git a/src/dexter.ts b/src/dexter.ts index 92d9749..cb5b856 100644 --- a/src/dexter.ts +++ b/src/dexter.ts @@ -18,6 +18,7 @@ import { SplitSwapRequest } from '@requests/split-swap-request'; import { TeddySwap } from '@dex/teddyswap'; import { Spectrum } from '@dex/spectrum'; import { SplitCancelSwapRequest } from '@requests/split-cancel-swap-request'; +import { MinswapV2 } from '@dex/minswap-v2'; export class Dexter { @@ -58,6 +59,7 @@ export class Dexter { this.metadataProvider = new TokenRegistryProvider(this.requestConfig); this.availableDexs = { [Minswap.identifier]: new Minswap(this.requestConfig), + [MinswapV2.identifier]: new MinswapV2(this.requestConfig), [SundaeSwap.identifier]: new SundaeSwap(this.requestConfig), [MuesliSwap.identifier]: new MuesliSwap(this.requestConfig), [WingRiders.identifier]: new WingRiders(this.requestConfig), diff --git a/src/index.ts b/src/index.ts index aca9736..3be17d1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,6 +40,7 @@ export * from './dex/models/dex-transaction'; export * from './dex/base-dex'; export * from './dex/minswap'; +export * from './dex/minswap-v2'; export * from './dex/sundaeswap'; export * from './dex/muesliswap'; export * from './dex/wingriders'; diff --git a/src/utils.ts b/src/utils.ts index 64bf1af..125d464 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,8 @@ import { Token } from '@dex/models/asset'; import { LiquidityPool } from '@dex/models/liquidity-pool'; +import { Lucid, Utils } from 'lucid-cardano'; + +export const lucidUtils: Utils = new Utils(new Lucid()); export function tokensMatch(tokenA: Token, tokenB: Token): boolean { const tokenAId: string = tokenA === 'lovelace' ? 'lovelace' : tokenA.identifier();