diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1ab5071..5d1d0e5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@ All notable changes to Dexter will be documented in this file.
## [UNRELEASED]
- TeddySwap integration
+- Spectrum integration
## [v4.2.0]
- Fix WR price impact formula for 0 decimals
diff --git a/README.md b/README.md
index 07a9745..ba14b81 100644
--- a/README.md
+++ b/README.md
@@ -7,6 +7,7 @@
+
### What You Can Do
diff --git a/src/dex/api/spectrum-api.ts b/src/dex/api/spectrum-api.ts
new file mode 100644
index 0000000..6e82199
--- /dev/null
+++ b/src/dex/api/spectrum-api.ts
@@ -0,0 +1,64 @@
+import { BaseApi } from './base-api';
+import { Asset, Token } from '../models/asset';
+import { LiquidityPool } from '../models/liquidity-pool';
+import axios, { AxiosInstance } from 'axios';
+import { RequestConfig } from '@app/types';
+import { appendSlash, tokensMatch } from '@app/utils';
+import { TeddySwap } from '@dex/teddyswap';
+import { Spectrum } from '@dex/spectrum';
+
+export class SpectrumApi extends BaseApi {
+
+ protected readonly api: AxiosInstance;
+ protected readonly dex: TeddySwap;
+
+ constructor(dex: TeddySwap, requestConfig: RequestConfig) {
+ super();
+
+ this.dex = dex;
+ this.api = axios.create({
+ timeout: requestConfig.timeout,
+ baseURL: `${appendSlash(requestConfig.proxyUrl)}https://analytics-balanced.spectrum.fi/cardano`,
+ headers: {
+ 'Content-Type': 'application/json',
+ }
+ });
+ }
+
+ liquidityPools(assetA: Token, assetB?: Token): Promise {
+ return this.api.get('/front/pools', ).then((response: any) => {
+ return response.data.map((poolResponse: any) => {
+ const tokenA: Token = poolResponse.lockedX.asset.currencySymbol !== ''
+ ? new Asset(poolResponse.lockedX.asset.currencySymbol, Buffer.from(poolResponse.lockedX.asset.tokenName, 'utf8').toString('hex'))
+ : 'lovelace';
+ const tokenB: Token = poolResponse.lockedY.asset.currencySymbol !== ''
+ ? new Asset(poolResponse.lockedY.asset.currencySymbol, Buffer.from(poolResponse.lockedY.asset.tokenName, 'utf8').toString('hex'))
+ : 'lovelace';
+
+ if (! tokensMatch(tokenA, assetA) || (assetB && ! tokensMatch(tokenB, assetB))) {
+ return undefined;
+ }
+
+ let liquidityPool: LiquidityPool = new LiquidityPool(
+ Spectrum.identifier,
+ tokenA,
+ tokenB,
+ BigInt(poolResponse.lockedX.amount),
+ BigInt(poolResponse.lockedY.amount),
+ '', // Not supplied
+ this.dex.orderAddress,
+ this.dex.orderAddress,
+ );
+
+ const [poolNftPolicyId, poolNftName] = poolResponse.id.split('.');
+ liquidityPool.poolNft = new Asset(poolNftPolicyId, Buffer.from(poolNftName, 'utf8').toString('hex'));
+ liquidityPool.lpToken = new Asset(poolResponse.lockedLQ.asset.currencySymbol, Buffer.from(poolResponse.lockedLQ.asset.tokenName, 'utf8').toString('hex'));
+ liquidityPool.poolFeePercent = (1 - (poolResponse.poolFeeNum / poolResponse.poolFeeDenum)) * 100;
+ liquidityPool.identifier = liquidityPool.lpToken.identifier();
+
+ return liquidityPool;
+ }).filter((pool: LiquidityPool | undefined) => pool !== undefined) as LiquidityPool[];
+ });
+ }
+
+}
diff --git a/src/dex/definitions/spectrum/order.ts b/src/dex/definitions/spectrum/order.ts
new file mode 100644
index 0000000..2b25d08
--- /dev/null
+++ b/src/dex/definitions/spectrum/order.ts
@@ -0,0 +1,85 @@
+import { DatumParameterKey } from '@app/constants';
+import { DatumParameters, DefinitionField } from '@app/types';
+
+export default {
+ constructor: 0,
+ fields: [
+ {
+ constructor: 0,
+ fields: [
+ {
+ bytes: DatumParameterKey.SwapInTokenPolicyId
+ },
+ {
+ bytes: DatumParameterKey.SwapInTokenAssetName
+ }
+ ],
+ },
+ {
+ constructor: 0,
+ fields: [
+ {
+ bytes: DatumParameterKey.SwapOutTokenPolicyId
+ },
+ {
+ bytes: DatumParameterKey.SwapOutTokenAssetName
+ }
+ ],
+ },
+ {
+ constructor: 0,
+ fields: [
+ {
+ bytes: DatumParameterKey.TokenPolicyId // Pool NFT
+ },
+ {
+ bytes: DatumParameterKey.TokenAssetName
+ }
+ ],
+ },
+ {
+ int: DatumParameterKey.LpFee
+ },
+ {
+ int: DatumParameterKey.LpFeeNumerator // Execution fee numerator
+ },
+ {
+ int: DatumParameterKey.LpFeeDenominator // Execution fee denominator
+ },
+ {
+ bytes: DatumParameterKey.SenderPubKeyHash
+ },
+ (field: DefinitionField, parameters: DatumParameters, shouldExtract: boolean = true) => {
+ if (! shouldExtract) {
+ const stakeKeyHash: string = parameters[DatumParameterKey.SenderStakingKeyHash] as string ?? null;
+
+ if (! stakeKeyHash) return;
+
+ return {
+ constructor: 0,
+ fields: [
+ {
+ bytes: stakeKeyHash,
+ }
+ ],
+ };
+ }
+
+ if ('fields' in field) {
+ if (field.constructor === 1) return;
+
+ if (field.fields.length > 0 && 'bytes' in field.fields[0]) {
+ parameters[DatumParameterKey.SenderStakingKeyHash] = field.fields[0].bytes;
+ }
+ }
+
+ return;
+ },
+ {
+ int: DatumParameterKey.SwapInAmount
+ },
+ {
+ int: DatumParameterKey.MinReceive
+ }
+ ],
+}
diff --git a/src/dex/definitions/spectrum/pool.ts b/src/dex/definitions/spectrum/pool.ts
new file mode 100644
index 0000000..e59b246
--- /dev/null
+++ b/src/dex/definitions/spectrum/pool.ts
@@ -0,0 +1,62 @@
+import { DatumParameterKey } from '@app/constants';
+
+export default {
+ constructor: 0,
+ fields: [
+ {
+ constructor: 0,
+ fields: [
+ {
+ bytes: DatumParameterKey.TokenPolicyId // Pool NFT
+ },
+ {
+ bytes: DatumParameterKey.TokenAssetName
+ }
+ ]
+ },
+ {
+ constructor: 0,
+ fields: [
+ {
+ bytes: DatumParameterKey.PoolAssetAPolicyId
+ },
+ {
+ bytes: DatumParameterKey.PoolAssetAAssetName
+ }
+ ]
+ },
+ {
+ constructor: 0,
+ fields: [
+ {
+ bytes: DatumParameterKey.PoolAssetBPolicyId
+ },
+ {
+ bytes: DatumParameterKey.PoolAssetBAssetName
+ }
+ ]
+ },
+ {
+ constructor: 0,
+ fields: [
+ {
+ bytes: DatumParameterKey.LpTokenPolicyId
+ },
+ {
+ bytes: DatumParameterKey.LpTokenAssetName
+ }
+ ]
+ },
+ {
+ int: DatumParameterKey.LpFee
+ },
+ [
+ {
+ bytes: DatumParameterKey.StakeAdminPolicy
+ }
+ ],
+ {
+ int: DatumParameterKey.LqBound
+ }
+ ]
+}
diff --git a/src/dex/logo/spectrum.png b/src/dex/logo/spectrum.png
new file mode 100644
index 0000000..c63b757
Binary files /dev/null and b/src/dex/logo/spectrum.png differ
diff --git a/src/dex/spectrum.ts b/src/dex/spectrum.ts
new file mode 100644
index 0000000..268fdc4
--- /dev/null
+++ b/src/dex/spectrum.ts
@@ -0,0 +1,242 @@
+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 {
+ AssetBalance,
+ DatumParameters,
+ DefinitionConstr,
+ DefinitionField,
+ PayToAddress,
+ RequestConfig,
+ SwapFee,
+ UTxO
+} from '@app/types';
+import { DefinitionBuilder } from '@app/definition-builder';
+import { AddressType, DatumParameterKey } from '@app/constants';
+import { BaseApi } from '@dex/api/base-api';
+import pool from './definitions/spectrum/pool';
+import order from './definitions/spectrum/order';
+import { correspondingReserves, tokensMatch } from '@app/utils';
+import { SpectrumApi } from '@dex/api/spectrum-api';
+
+const MAX_INT: bigint = 9_223_372_036_854_775_807n;
+
+export class Spectrum extends BaseDex {
+
+ public static readonly identifier: string = 'Spectrum';
+ public readonly api: BaseApi;
+
+ /**
+ * On-Chain constants.
+ */
+ public readonly orderAddress: string = 'addr1wynp362vmvr8jtc946d3a3utqgclfdl5y9d3kn849e359hsskr20n';
+
+ constructor(requestConfig: RequestConfig = {}) {
+ super();
+
+ this.api = new SpectrumApi(this, requestConfig);
+ }
+
+ public async liquidityPoolAddresses(provider: BaseDataProvider): Promise {
+ return Promise.resolve([
+ 'addr1x94ec3t25egvhqy2n265xfhq882jxhkknurfe9ny4rl9k6dj764lvrxdayh2ux30fl0ktuh27csgmpevdu89jlxppvrst84slu',
+ 'addr1x8nz307k3sr60gu0e47cmajssy4fmld7u493a4xztjrll0aj764lvrxdayh2ux30fl0ktuh27csgmpevdu89jlxppvrswgxsta',
+ ]);
+ }
+
+ async liquidityPools(provider: BaseDataProvider): Promise {
+ const poolAddresses: string[] = await this.liquidityPoolAddresses(provider);
+
+ const addressPromises: Promise[] = poolAddresses.map(async (address: string) => {
+ const utxos: UTxO[] = await provider.utxos(address);
+
+ 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 = utxo.assetBalances.filter((assetBalance: AssetBalance) => {
+ const assetName = assetBalance.asset === 'lovelace' ? 'lovelace' : assetBalance.asset.assetName;
+ return !assetName?.toLowerCase()?.endsWith('_nft')
+ && !assetName?.toLowerCase()?.endsWith('_identity')
+ && !assetName?.toLowerCase()?.endsWith('_lq');
+ });
+
+ // Irrelevant UTxO
+ if (![2, 3].includes(relevantAssets.length)) {
+ 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(
+ Spectrum.identifier,
+ relevantAssets[assetAIndex].asset,
+ relevantAssets[assetBIndex].asset,
+ relevantAssets[assetAIndex].quantity,
+ relevantAssets[assetBIndex].quantity,
+ utxo.address,
+ this.orderAddress,
+ this.orderAddress
+ );
+
+ 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);
+
+ const [lpTokenPolicyId, lpTokenAssetName] = typeof parameters.LpTokenPolicyId === 'string' && typeof parameters.LpTokenAssetName === 'string'
+ ? [parameters.LpTokenPolicyId, parameters.LpTokenAssetName]
+ : [null, null];
+ const lpTokenBalance: AssetBalance | undefined = utxo.assetBalances.find((assetBalance: AssetBalance) => {
+ return assetBalance.asset !== 'lovelace'
+ && assetBalance.asset.policyId === lpTokenPolicyId
+ && assetBalance.asset.nameHex === lpTokenAssetName;
+ });
+ const nftToken: Asset | undefined = utxo.assetBalances.find((assetBalance) => {
+ return (assetBalance.asset as Asset).assetName?.toLowerCase()?.endsWith('_nft');
+ })?.asset as Asset | undefined;
+
+ if (! lpTokenBalance || ! nftToken) {
+ return Promise.resolve(undefined);
+ }
+
+ liquidityPool.poolNft = nftToken;
+ liquidityPool.lpToken = lpTokenBalance.asset as Asset;
+ liquidityPool.totalLpTokens = MAX_INT - lpTokenBalance.quantity;
+ liquidityPool.identifier = liquidityPool.lpToken.identifier();
+ liquidityPool.poolFeePercent = typeof parameters.LpFee === 'number' ? (1000 - parameters.LpFee) / 10 : 0.3;
+ } catch (e) {
+ return liquidityPool;
+ }
+
+ return liquidityPool;
+ }
+
+ estimatedGive(liquidityPool: LiquidityPool, swapOutToken: Token, swapOutAmount: bigint): bigint {
+ const [reserveOut, reserveIn]: bigint[] = correspondingReserves(liquidityPool, swapOutToken);
+
+ const receive: bigint = (reserveIn * reserveOut) / (reserveOut - swapOutAmount) - reserveIn;
+ const swapFee: bigint = ((receive * BigInt(Math.floor(liquidityPool.poolFeePercent * 100))) + BigInt(10000) - 1n) / 10000n;
+
+ return receive + swapFee;
+ }
+
+ estimatedReceive(liquidityPool: LiquidityPool, swapInToken: Token, swapInAmount: bigint): bigint {
+ const [reserveIn, reserveOut]: bigint[] = correspondingReserves(liquidityPool, swapInToken);
+
+ const swapFee: bigint = ((swapInAmount * BigInt(Math.floor(liquidityPool.poolFeePercent * 100))) + BigInt(10000) - 1n) / 10000n;
+
+ return reserveOut - (reserveIn * reserveOut) / (reserveIn + swapInAmount - swapFee);
+ }
+
+ priceImpactPercent(liquidityPool: LiquidityPool, swapInToken: Token, swapInAmount: bigint): number {
+ const reserveIn: bigint = tokensMatch(swapInToken, liquidityPool.assetA)
+ ? liquidityPool.reserveA
+ : liquidityPool.reserveB;
+
+ return (1 - (Number(reserveIn) / Number(reserveIn + swapInAmount))) * 100;
+ }
+
+ public async buildSwapOrder(liquidityPool: LiquidityPool, swapParameters: DatumParameters, spendUtxos: UTxO[] = []): 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');
+ const minReceive = swapParameters.MinReceive as bigint;
+
+ if (! batcherFee || ! deposit || ! minReceive) {
+ return Promise.reject('Parameters for datum are not set.');
+ }
+ if (! liquidityPool.poolNft) {
+ return Promise.reject('Pool NFT is required.');
+ }
+
+ const decimalToFractionalImproved = (decimalValue: bigint | number): [bigint, bigint] => {
+ const [whole, decimals = ''] = decimalValue.toString()?.split('.');
+ let truncatedDecimals = decimals.slice(0, 15);
+ const denominator: bigint = BigInt(10 ** truncatedDecimals.length);
+ const numerator = BigInt(whole) * denominator + BigInt(decimals);
+ return [numerator, denominator];
+ }
+
+ const batcherFeeForToken = Number(batcherFee.value) / Number(minReceive);
+ const [numerator, denominator] = decimalToFractionalImproved(batcherFeeForToken);
+ const lpfee: bigint = BigInt(1000 - Math.floor(liquidityPool.poolFeePercent * 10));
+
+ swapParameters = {
+ ...swapParameters,
+ [DatumParameterKey.TokenPolicyId]: liquidityPool.poolNft.policyId,
+ [DatumParameterKey.TokenAssetName]: liquidityPool.poolNft.nameHex,
+ [DatumParameterKey.LpFee]: lpfee,
+ [DatumParameterKey.LpFeeNumerator]: numerator,
+ [DatumParameterKey.LpFeeDenominator]: denominator,
+ };
+
+ const datumBuilder: DefinitionBuilder = new DefinitionBuilder();
+ await datumBuilder.loadDefinition(order).then((builder: DefinitionBuilder) => {
+ builder.pushParameters(swapParameters);
+ });
+
+ return [
+ this.buildSwapOrderPayment(swapParameters, {
+ address: this.orderAddress,
+ addressType: AddressType.Contract,
+ assetBalances: [
+ {
+ asset: 'lovelace',
+ quantity: batcherFee?.value + deposit.value,
+ },
+ ],
+ datum: datumBuilder.getCbor(),
+ isInlineDatum: true,
+ spendUtxos: spendUtxos,
+ }),
+ ];
+ }
+
+ public buildCancelSwapOrder(txOutputs: UTxO[], returnAddress: string): Promise {
+ throw new Error('Method not implemented.');
+ }
+
+ public swapOrderFees(): SwapFee[] {
+ const networkFee: number = 0.5;
+ const reward: number = 1;
+ const minNitro: number = 1.2;
+ const batcherFee: number = (reward + networkFee) * minNitro;
+ const batcherFeeInAda: bigint = BigInt(Math.round(batcherFee * 10 ** 6));
+
+ return [
+ {
+ id: 'batcherFee',
+ title: 'Batcher Fee',
+ description: 'Fee paid for the service of off-chain batcher to process transactions.',
+ value: batcherFeeInAda,
+ 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 cef6057..c6c3f9a 100644
--- a/src/dexter.ts
+++ b/src/dexter.ts
@@ -16,6 +16,7 @@ import axios from "axios";
import axiosRetry from "axios-retry";
import { SplitSwapRequest } from '@requests/split-swap-request';
import { TeddySwap } from '@dex/teddyswap';
+import { Spectrum } from '@dex/spectrum';
export class Dexter {
@@ -61,6 +62,7 @@ export class Dexter {
[WingRiders.identifier]: new WingRiders(this.requestConfig),
[VyFinance.identifier]: new VyFinance(this.requestConfig),
[TeddySwap.identifier]: new TeddySwap(this.requestConfig),
+ [Spectrum.identifier]: new Spectrum(this.requestConfig),
};
}
diff --git a/src/index.ts b/src/index.ts
index d57761b..946a0e7 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -44,3 +44,4 @@ export * from './dex/muesliswap';
export * from './dex/wingriders';
export * from './dex/vyfinance';
export * from './dex/teddyswap';
+export * from './dex/spectrum';
diff --git a/tests/spectrum.test.ts b/tests/spectrum.test.ts
new file mode 100644
index 0000000..d08fb85
--- /dev/null
+++ b/tests/spectrum.test.ts
@@ -0,0 +1,100 @@
+import {
+ Asset,
+ Dexter,
+ LiquidityPool,
+ MockDataProvider,
+ SwapRequest,
+ MockWalletProvider,
+ DatumParameters,
+ DatumParameterKey,
+ PayToAddress,
+ AddressType,
+} from '../src';
+import { Spectrum } from '../src/dex/spectrum';
+
+describe('Spectrum', () => {
+
+ const walletProvider: MockWalletProvider = new MockWalletProvider();
+ walletProvider.loadWalletFromSeedPhrase(['']);
+ const dexter: Dexter = (new Dexter())
+ .withDataProvider(new MockDataProvider())
+ .withWalletProvider(walletProvider);
+ const asset: Asset = new Asset('f66d78b4a3cb3d37afa0ec36461e51ecbde00f26c8f0a68f94b69880', '69555344', 6);
+
+ describe('Set Swap In', () => {
+
+ const liquidityPool: LiquidityPool = new LiquidityPool(
+ Spectrum.identifier,
+ 'lovelace',
+ asset,
+ 15853604203n,
+ 2999947840n,
+ 'addr1',
+ );
+ liquidityPool.poolFeePercent = 0.3;
+ liquidityPool.lpToken = new Asset('f66d78b4a3cb3d37afa0ec36461e51ecbde00f26c8f0a68f94b69880', '69555344');
+
+ const swapRequest: SwapRequest = dexter.newSwapRequest()
+ .forLiquidityPool(liquidityPool)
+ .withSwapInToken('lovelace')
+ .withSwapInAmount(10_000_000000n)
+ .withSlippagePercent(1.0);
+
+ it('Can calculate swap parameters', () => {
+ expect(+swapRequest.getPriceImpactPercent().toFixed(2)).toEqual(38.68);
+ expect(swapRequest.getEstimatedReceive()).toEqual(1158222522n);
+ expect(swapRequest.getMinimumReceive()).toEqual(1146754972n);
+ });
+
+ it('Can build swap order', () => {
+ const spectrum: Spectrum = new Spectrum();
+ const defaultSwapParameters: DatumParameters = {
+ [DatumParameterKey.PoolIdentifier]: '1234',
+ [DatumParameterKey.SenderPubKeyHash]: walletProvider.publicKeyHash(),
+ [DatumParameterKey.SenderStakingKeyHash]: walletProvider.stakingKeyHash(),
+ [DatumParameterKey.ReceiverPubKeyHash]: walletProvider.publicKeyHash(),
+ [DatumParameterKey.ReceiverStakingKeyHash]: walletProvider.stakingKeyHash(),
+ [DatumParameterKey.SwapInAmount]: swapRequest.swapInAmount,
+ [DatumParameterKey.MinReceive]: swapRequest.getMinimumReceive(),
+ [DatumParameterKey.SwapInTokenPolicyId]: '',
+ [DatumParameterKey.SwapInTokenAssetName]: '',
+ [DatumParameterKey.SwapOutTokenPolicyId]: asset.policyId,
+ [DatumParameterKey.SwapOutTokenAssetName]: asset.nameHex,
+ };
+
+ return spectrum.buildSwapOrder(liquidityPool, defaultSwapParameters)
+ .then((payments: PayToAddress[]) => {
+ expect(() => { spectrum.buildSwapOrder(liquidityPool, defaultSwapParameters); }).not.toThrowError();
+ expect(payments[0].addressType).toBe(AddressType.Contract);
+ expect(payments[0].assetBalances[0].quantity).toEqual(10003800000n);
+ expect(payments[0].datum).toBe('d8799fd8799f4040ffd8799f581cf66d78b4a3cb3d37afa0ec36461e51ecbde00f26c8f0a68f94b698804469555344ffd8799f581cf66d78b4a3cb3d37afa0ec36461e51ecbde00f26c8f0a68f94b698804469555344ff1903e51b0000016d7639cd581b00038d7ea4c6800042ed56d8799f42bac6ff1b00000002540be4001a445a179cff');
+ });
+ });
+
+ });
+
+ describe('Set Swap Out', () => {
+
+ const liquidityPool: LiquidityPool = new LiquidityPool(
+ Spectrum.identifier,
+ 'lovelace',
+ asset,
+ 15853604203n,
+ 2999947840n,
+ 'addr1',
+ );
+ liquidityPool.poolFeePercent = 0.3;
+
+ const swapRequest: SwapRequest = dexter.newSwapRequest()
+ .forLiquidityPool(liquidityPool)
+ .withSwapInToken('lovelace')
+ .withSwapOutAmount(1_000_000000n)
+ .withSlippagePercent(1.0);
+
+ it('Can calculate swap parameters', () => {
+ expect(swapRequest.swapInAmount).toEqual(7950789864n);
+ });
+
+ });
+
+});