This module can be used to get information about Magicswap pools, allowing you to perform token swaps and manage pool liquidity.

Methods

interface ApiClient {
  // getters
  getPools(): Promise<PoolsReply>;
  getPool(id: string): Promise<PoolReply>;
  getRoute(body: RouteBody): Promise<RouteReply>;

  // actions
  swap(
    body: SwapBody,
    waitForCompletion?: boolean,
  ): Promise<CreateTransactionReply | void>;
  addLiquidity(
    poolId: string,
    body: AddLiquidityBody,
    waitForCompletion?: boolean,
  ): Promise<CreateTransactionReply | void>;
  removeLiquidity(
    poolId: string,
    body: RemoveLiquidityBody,
    waitForCompletion?: boolean,
  ): Promise<CreateTransactionReply | void>;
}

Usage

See examples/magicswap for usage examples

Swap and liquidity operations require the user’s smart wallet to be connected and a started session (See Connect & Identity)

Performing a swap

Step 1: Fetch pool and route data

// fetch any necessary pool data, including token ids for nfts
const poolData = await tdk.magicswap.getPool(/* your pool id */);

// calculate swap route by specifying the types of tokens to swap and the desired output amount
const routeData = await tdk.magicswap.getRoute(
    tokenInId: poolData.token0.id, // token you want to swap from
    tokenOutId: poolData.token1.id, // token you want to swap to
    amount: "1", // amount of tokens you are swapping or receiving
    isExactOut: true // true if you want to receive `amount` tokens (see below)
);

If isExactOut = true then amount is the amount out. If isExactOut = false then amount is the amount in. See this section below for details.

Step 1.1: For swaps that involve NFTs

The user must select the NFTs to swap (and their quantities)

  • if the input token is NFT, select from routeData.tokenIn.collectionTokenIds up to routeData.amountIn NFTs
  • if the output token is NFT, select from routeData.tokenOut.collectionTokenIds up to routeData.amountOut NFTs
// let's say the user picked the first one from the list as output
const userSelectedNFTsOutput = [
  {
    id: routeData.tokenOut.collectionTokenIds[0],
    quantity: 1,
  },
];

Step 2: Perform the swap

// use route data and user selection to build the body
const swapBody = {
  tokenInId: routeData.tokenIn.id,
  tokenOutId: routeData.tokenOut.id,
  path: routeData.path,
  amountIn: routeData.amountIn,
  nftsOut: userSelectedNFTsOutput,
  isExactOut: true,
};
// send request to perform swap (ERC20 to NFT swap in this example)
const transaction = await tdk.magicswap.swap(swapBody);

Other types of swap

Different properties of SwapBody can be used for different kinds of swap:

Swap typeProperties
ERC20 to NFTamountIn & nftsOut
ERC20 to ERC20amountIn & amountOut
NFT to NFTnftsIn & nftsOut
NFT to ERC20nftsIn & amountOut

The isExactOut parameter

When calling getRoute and swap you must specify the isExactOut parameter. The same value should be used for both.

isExactOut = true means that the user has specified the exact amount of tokens they want to receive.

There are a few different scenarios:

  • ERC20⇔NFT swaps
    • If the user is trading their NFTs, isExactOut = false because they must specify exactly how many NFTs they are putting in.
    • If the user is trading to NFTs, isExactOut = true because they must specify exactly how many NFTs they are receiving.
  • ERC20⇔ERC20 or NFT⇔NFT swaps; isExactOut depends on whatever the user specified.
    • For example, if you’re swapping MAGIC to VEE, then isExactOut = true if you specify how much VEE you want, and isExactOut = false if you specify how much MAGIC you will give.

Adding Liquidity

// helper function to calculate quote
const getQuote = (amountA: bigint, reserveA: bigint, reserveB: bigint) =>
  reserveA > 0 ? (amountA * reserveB) / reserveA : 0n;

// helper function to calculate min amount
const getAmountMin = (amount: bigint, slippage: number) =>
  amount - (amount * BigInt(Math.ceil(slippage * 1000))) / 1000n;

const poolData = await tdk.magicswap.getPool(/* your pool id */);
// for tokenA we should know the amount we want to add (amountA = 1 in this case)
// for tokenB we can calculate amountB based on amountA and the pool reserves
const tokenA = poolData.token1;
const tokenB = poolData.token0;
const reserveA = tokenA.reserve;
const reserveB = tokenB.reserve;
const amountA = BigInt(1);
const amountB = getQuote(
  parseUnits(amountA.toString(), tokenA.decimals),
  BigInt(reserveA),
  BigInt(reserveB),
);

const addLiquidityBody = {
  amount0: amountB.toString(),
  amount0Min: getAmountMin(amountB, 0.01).toString(),
  nfts1: [
    {
      id: tokenA.collectionTokenIds[0],
      quantity: Number(amountA),
    },
  ],
};

// send request to add liquidity (ERC20⇔NFT pool in this example)
var transaction = await tdk.magicswap.addLiquidity(
  poolData.id,
  addLiquidityBody,
);

Other types of pools

Different properties of AddLiquidityBody can be used for different kinds of pools:

Pool typeProperties
ERC20⇔NFTamount0 & amount0Min & nfts1
ERC20⇔ERC20amount0 & amount0Min & amount1 & amount1Min
NFT⇔NFTnfts0 & nfts1
NFT⇔ERC20nfts0 & amount1 & amount1Min

Removing Liquidity

const getAmountMin = (amount: bigint, slippage: number) =>
  amount - (amount * BigInt(Math.ceil(slippage * 1000))) / 1000n;

const poolData = await tdk.magicswap.getPool(/* your pool id */);
const reserve0 = BigInt(poolData.token0.reserve);
const reserve1 = BigInt(poolData.token1.reserve);
const totalSupply = BigInt(poolData.totalSupply);

const rawNftsDesired = "1";

const nftsDesired = parseUnits(rawNftsDesired, poolData.token1.decimals);
// calculate LP amount to get the desired amount of nfts (1 in this example).
// the LP amount could otherwise be inputted by the user.
const product = nftsDesired * totalSupply;
// Compute the quotient and remainder
let amountLPWei = product / reserve1;
const reminder = product % reserve1;
if (reminder > 0) {
  amountLPWei += BigInt(1);
}

const amount0 = (amountLPWei * reserve0) / totalSupply;

// use helper to account for slippage for amount0 since it's an ERC20 token
const amount0Min = getAmountMin(amount0, 0.01);

const removeLiquidityBody = {
  amountLP: amountLPWei.toString(),
  amount0Min: amount0Min.toString(),
  amount1Min: nftsDesired.toString(),
  nfts1: [
    {
      id: poolData.token1.collectionTokenIds[0],
      quantity: Number(rawNftsDesired),
    },
  ],
};
await tdk.magicswap.removeLiquidity(poolData.id, removeLiquidityBody);