Magicswap
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
examples/magicswap
for usage examplesSwap 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 torouteData.amountIn
NFTs - if the output token is NFT, select from
routeData.tokenOut.collectionTokenIds
up torouteData.amountOut
NFTs
// lets 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 type | Properties |
---|---|
ERC20 to NFT | amountIn & nftsOut |
ERC20 to ERC20 | amountIn & amountOut |
NFT to NFT | nftsIn & nftsOut |
NFT to ERC20 | nftsIn & 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.
- If the user is trading their NFTs,
- 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, andisExactOut = false
if you specify how much MAGIC you will give.
- For example, if you’re swapping MAGIC to VEE, then
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 type | Properties |
---|---|
ERC20⇔NFT | amount0 & amount0Min & nfts1 |
ERC20⇔ERC20 | amount0 & amount0Min & amount1 & amount1Min |
NFT⇔NFT | nfts0 & nfts1 |
NFT⇔ERC20 | nfts0 & 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 its 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);