Skip to content

minswap/sdk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

65 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Minswap SDK

The Minswap open-source project provides a comprehensive suite of off-chain tools, including price feeds, historical data storage, and integration methods for Stableswap, AMM V1, AMM V2, and LBE V2.

  • See the Minswap Example for a demonstration of how to use the SDK.
  • For additional transaction-building examples, refer to the examples/ directory in this repository.

Features

  • Pool price feed
  • Historical data of pool
  • Calculate trade price
  • Calculate price impact
  • Create orders and submit with Lucid
  • Syncer to sync minswap's liquidity pool data

We provide multiple adapters to get the price and liquidity pool information.

  • BlockfrostAdapter: use Blockfrost to query the data.
  • MinswapAdapter: use Syncer to query the data. If you want to use MinswapAdapter you need to run syncer by yourself.
  • MaestroAdapter: use Maestro to query the data.

Install

  • NPM: npm install @minswap/sdk
  • Yarn: yarn add @minswap/sdk

ES Module Requirement

This package depends on @spacebudz/lucid, which is an ESM package, so it's also an ESM package. To import from ESM package, you need to specify "type": "module" in package.json and configure other build flags accordingly. Also, --experimental-wasm-modules flag needs to be set in Node.js and @jsr:registry=https://npm.jsr.io needs to be added to .npmrc file.

Examples

Create an adapter using either BlockfrostAdapter or MinswapAdapter:

BlockfrostAdapter:

import { BlockFrostAPI } from "@blockfrost/blockfrost-js";
import { BlockfrostAdapter, NetworkId } from "@minswap/sdk";

const blockFrostApi = new BlockFrostAPI({
  projectId: "<your_project_id>",
  network: "mainnet",
});

const blockfrostAdapter = new BlockfrostAdapter(
  NetworkId.MAINNET,
  blockFrostApi
);

MinswapAdapter:

  • Install docker compose.
  • Update the .env file to specify the exact network you want to sync.
  • Run the command: docker compose -f docker-compose.yaml up --build -d to build.
  • Run the command: docker compose -f docker-compose.yaml logs -f to view log.
import { BlockFrostAPI } from "@blockfrost/blockfrost-js";
import {
  BlockfrostAdapter,
  MinswapAdapter,
  NetworkEnvironment,
  NetworkId,
  newPrismaClient,
  PostgresRepositoryReader,
} from "@minswap/sdk";

const blockFrostApi = new BlockFrostAPI({
  projectId: "<your_project_id>",
  network: "mainnet",
});

const prismaClient = await newPrismaClient(
  "postgresql://postgres:minswap@postgres:5432/syncer?schema=public&connection_limit=5"
);

const repositoryReader = new PostgresRepositoryReader(
  NetworkEnvironment.MAINNET,
  prismaClient
);

const minswapAdapter = new MinswapAdapter({
  networkId: NetworkId.MAINNET,
  networkEnv: NetworkEnvironment.MAINNET,
  blockFrostApi: blockFrostApi,
  repository: repositoryReader,
});

Maestro Adapter:

import { MaestroAdapter } from "@minswap/sdk";
import { MaestroClient, Configuration } from "@maestro-org/typescript-sdk";

const maestroClient = new MaestroClient(
  new Configuration({
    apiKey: maestroApiKey,
    network: cardanoNetwork,
  })
);

const maestroAdapter = new MaestroAdapter(NetworkId.TESTNET, maestroClient);

Example 1: Get current price of MIN/ADA pool

MIN/ADA pool v1:

for (let i = 1; ; i++) {
  const pools = await adapter.getV1Pools({
    page: i,
  });
  if (pools.length === 0) {
    // last page
    break;
  }
  const minAdaPool = pools.find(
    (p) =>
      p.assetA === "lovelace" &&
      p.assetB ===
        "29d222ce763455e3d7a09a665ce554f00ac89d2e99a1a83d267170c64d494e"
  );
  if (minAdaPool) {
    const [price0, price1] = await adapter.getV1PoolPrice({ pool: minAdaPool });
    console.log(
      `ADA/MIN price: ${price0.toString()}; MIN/ADA price: ${price1.toString()}`
    );
    // we can later use this ID to call getPoolById
    console.log(`ADA/MIN pool ID: ${minAdaPool.id}`);
    break;
  }
}

MIN/ADA pool v2:

const minAdaPool = await adapter.getV2PoolByPair(
  Asset.fromString("lovelace"),
  Asset.fromString(
    "29d222ce763455e3d7a09a665ce554f00ac89d2e99a1a83d267170c64d494e"
  )
);

if (minAdaPool) {
  const [a, b] = await adapter.getV2PoolPrice({ pool: minAdaPool });
  console.log(`ADA/MIN price: ${a.toString()}; MIN/ADA price: ${b.toString()}`);
}

Example 2: Get historical prices of MIN/ADA pool

MIN/ADA pool v1:

const MIN_ADA_POOL_ID =
  "6aa2153e1ae896a95539c9d62f76cedcdabdcdf144e564b8955f609d660cf6a2";

const history = await adapter.getV1PoolHistory({ id: MIN_ADA_POOL_ID });
for (const historyPoint of history) {
  const pool = await adapter.getV1PoolInTx({ txHash: historyPoint.txHash });
  if (!pool) {
    throw new Error("pool not found");
  }
  const [price0, price1] = await adapter.getV1PoolPrice({ pool: pool });
  console.log(`${historyPoint.time}: ${price0} ADA/MIN, ${price1} MIN/ADA`);
}

MIN/ADA pool v2:

for (let i = 1; ; i++) {
  const pools = await adapter.getV2PoolHistory({
    assetA: Asset.fromString("lovelace"),
    assetB: Asset.fromString(
      "29d222ce763455e3d7a09a665ce554f00ac89d2e99a1a83d267170c64d494e"
    ),
    page: i,
  });
  if (pools.length === 0) {
    // last page
    break;
  }

  for (const pool of pools) {
    const [price0, price1] = await adapter.getV2PoolPrice({ pool: pool });
    console.log(
      `ADA/MIN price: ${a.toString()}; MIN/ADA price: ${price1.toString()}`
    );
  }
}

Example 3: Build Order transaction and submit

  • Swap MIN-ADA in V2 pool on testnet:

With Blockfrost adapter:

import {
    ADA, Asset,
    BlockfrostAdapter,
    calculateAmountWithSlippageTolerance,
    DexV2,
    DexV2Calculation,
    getBackendBlockfrostLucidInstance,
    NetworkId, 
    OrderV2
} from "@minswap/sdk";
import {BlockFrostAPI} from "@blockfrost/blockfrost-js";

export async function swapExactInV2TxExample() {
    const networkId: NetworkId = NetworkId.TESTNET;
    const blockfrostProjectId = "<YOUR_BLOCKFROST_PROJECT_ID>";
    const blockfrostUrl = "https://cardano-preprod.blockfrost.io/api/v0";

    const address = "<YOUR_ADDRESS";

    const lucid = await getBackendBlockfrostLucidInstance(
        networkId,
        blockfrostProjectId,
        blockfrostUrl,
        address
    );

    const adapter = new BlockfrostAdapter(
        NetworkId.TESTNET,
        new BlockFrostAPI({
            projectId: blockfrostProjectId,
            network: "preprod",
        })
    );

    const MIN: Asset = {
        policyId: "e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed72",
        tokenName: "4d494e",
    };

    const utxos = await lucid.utxosAt(address);

    const assetA = ADA;
    const assetB = MIN;

    const pool = await adapter.getV2PoolByPair(assetA, assetB);
    if (!pool) {
        throw new Error("Pool not found");
    }

    const swapAmount = 1_000n;
    const amountOut = DexV2Calculation.calculateAmountOut({
        reserveIn: pool.reserveA,
        reserveOut: pool.reserveB,
        amountIn: swapAmount,
        tradingFeeNumerator: pool.feeA[0],
    });

    // 20% slippage tolerance
    const acceptedAmountOut = calculateAmountWithSlippageTolerance({
        slippageTolerancePercent: 20,
        amount: amountOut,
        type: "down",
    });

    const txComplete = await new DexV2(lucid, adapter).createBulkOrdersTx({
        sender: address,
        availableUtxos: utxos,
        orderOptions: [
            {
                type: OrderV2.StepType.SWAP_EXACT_IN,
                amountIn: swapAmount,
                assetIn: assetA,
                direction: OrderV2.Direction.A_TO_B,
                minimumAmountOut: acceptedAmountOut,
                lpAsset: pool.lpAsset,
                isLimitOrder: false,
                killOnFailed: false,
            },
        ],
    });

    const signedTx = await txComplete
        .signWithPrivateKey("<YOUR_PRIVATE_KEY>")
        .commit();
    const txId = await signedTx.submit();

    console.log(`Transaction submitted successfully: ${txId}`);
}

void swapExactInV2TxExample();

With Meastro adapter:

import {
    ADA,
    Asset,
    calculateAmountWithSlippageTolerance,
    DexV2,
    DexV2Calculation,
    getBackendMaestroLucidInstance,
    MaestroAdapter,
    NetworkId,
    OrderV2
} from "@minswap/sdk";
import {Configuration, MaestroClient} from "@maestro-org/typescript-sdk";

export async function swapExactInV2TxExample() {
    const networkId: NetworkId = NetworkId.TESTNET;
    const maestroApiKey = "<YOUR_API_KEY>";

    const address = "<YOUR_ADDRESS>";

    const lucid = await getBackendMaestroLucidInstance(
        "Preprod",
        maestroApiKey,
        address
    );

    const maestroClient = new MaestroClient(
        new Configuration({
            apiKey: maestroApiKey,
            network: "Preprod",
        })
    );

    const MIN: Asset = {
        policyId: "e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed72",
        tokenName: "4d494e",
    };

    const adapter = new MaestroAdapter(NetworkId.TESTNET, maestroClient);

    const utxos = await lucid.utxosAt(address);

    const assetA = ADA;
    const assetB = MIN;

    const pool = await adapter.getV2PoolByPair(assetA, assetB);
    if (!pool) {
        throw new Error("Pool not found");
    }

    const swapAmount = 1_000n;
    const amountOut = DexV2Calculation.calculateAmountOut({
        reserveIn: pool.reserveA,
        reserveOut: pool.reserveB,
        amountIn: swapAmount,
        tradingFeeNumerator: pool.feeA[0],
    });

    // 20% slippage tolerance
    const acceptedAmountOut = calculateAmountWithSlippageTolerance({
        slippageTolerancePercent: 20,
        amount: amountOut,
        type: "down",
    });

    const txComplete = await new DexV2(lucid, adapter).createBulkOrdersTx({
        sender: address,
        availableUtxos: utxos,
        orderOptions: [
            {
                type: OrderV2.StepType.SWAP_EXACT_IN,
                amountIn: swapAmount,
                assetIn: assetA,
                direction: OrderV2.Direction.A_TO_B,
                minimumAmountOut: acceptedAmountOut,
                lpAsset: pool.lpAsset,
                isLimitOrder: false,
                killOnFailed: false,
            },
        ],
    });

    const signedTx = await txComplete
        .signWithPrivateKey("<YOUR_PRIVATE_KEY>")
        .commit();
    const txId = await signedTx.submit();

    console.log(`Transaction submitted successfully: ${txId}`);
}

void swapExactInV2TxExample();

See examples/ or docs for more details. You can run a single file like npm run exec examples/example.ts.