This module can be used to get information about Magicswap pools, allowing you to perform token swaps and manage pool liquidity.
Public members
Assets/Treasure/TDK/Runtime/Magicswap/TDK.Magicswap.cs
// getters
public async Task<List<MagicswapPool>> GetAllPools();
public async Task<MagicswapPool> GetPoolById(string id);
public async Task<MagicswapRoute> GetRoute(string tokenInId, string tokenOutId, string amount, bool isExactOut);
// actions
public async Task<Transaction> Swap(SwapBody swapBody);
public async Task<Transaction> AddLiquidity(string poolId, AddLiquidityBody addLiquidityBody);
public async Task<Transaction> RemoveLiquidity(string poolId, RemoveLiquidityBody removeLiquidityBody);
// liquidity helpers
public BigInteger GetQuote(BigInteger amountA, BigInteger reserveA, BigInteger reserveB);
public BigInteger GetAmountMin(BigInteger amount, double slippage);
// allowance helpers
public async Task<BigInteger> GetERC20Allowance(string tokenAddress, string address);
public async Task<bool> IsERC20Approved(string tokenAddress, string address, BigInteger amount);
public async Task<bool> IsERC1155Approved(string tokenAddress, string address);
public async Task<bool> IsERC721Approved(string tokenAddress, string address);
Usage
Swap and liquidity operations require the user’s smart wallet to be connected
and a started session (See Connect & Identity)
Token Approval
In order to approve tokens and NFTs for transfer when swapping or adding liquidity,
the token addresses must be added to the callTargets
array in the config JSON or TDKConfig
asset.
LP token addresses (pool ids) must also be added as callTargets
in order to approve for transfer when removing liquidity.
Tokens can be approved using the TDK.Common.ApproveERC*
helpers. For example:
var magicswapRouterAddress = TDK.Common.GetContractAddress(Contract.MagicswapV2Router);
// approving ERC20
var magicAddress = TDK.Common.GetContractAddress(Contract.Magic);
await TDK.Common.ApproveERC20(magicAddress, magicswapRouterAddress, amount);
await TDK.Common.ApproveERC20(poolData.id, magicswapRouterAddress, amount);
// approving ERC1155
await TDK.Common.ApproveERC1155(/* ERC1155 token address */, magicswapRouterAddress);
Step 1: Fetch pool and route data
using Treasure;
// fetch any necessary pool data, including token ids for nfts
var poolData = await TDK.Magicswap.GetPoolById(/* your pool id */);
// calculate swap route by specifying the types of tokens to swap and the desired output amount
var 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
var userSelectedNFTsOutput = new List<NFTInput> {
new() {
id = routeData.tokenOut.collectionTokenIds[0],
quantity = 1,
}
};
// use route data and user selection to build the body
var swapBody = new 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)
var transaction = await TDK.Magicswap.Swap(swapBody);
// check the result
var success = transaction.status == "mined";
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.
- 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
var poolData = await TDK.Magicswap.GetPoolById(/* 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
var tokenA = poolData.token1;
var tokenB = poolData.token0;
var amountA = new BigInteger(1);
var reserveA = BigInteger.Parse(tokenA.reserve);
var reserveB = BigInteger.Parse(tokenB.reserve);
var amountB = TDK.Magicswap.GetQuote(
amountA.AdjustDecimals(0, tokenA.decimals),
reserveA,
reserveB
);
var addLiquidityBody = new AddLiquidityBody {
amount0 = amountB.ToString(),
amount0Min = TDK.Magicswap.GetAmountMin(amountB, 0.01).ToString(),
nfts1 = new List<NFTInput>() {
new() {
id = tokenA.collectionTokenIds[0],
quantity = (int)amountA,
}
}
};
// send request to add liquidity (ERC20⇔NFT pool in this example)
var transaction = await TDK.Magicswap.AddLiquidity(poolData.id, addLiquidityBody);
// check the result
var success = transaction.status == "mined";
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
var poolData = await TDK.Magicswap.GetPoolById(/* your pool id */);
var reserve0 = BigInteger.Parse(poolData.token0.reserve);
var reserve1 = BigInteger.Parse(poolData.token1.reserve);
var totalSupply = BigInteger.Parse(poolData.totalSupply);
var nftsDesired = new BigInteger(1).AdjustDecimals(0, 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.
var amountLPWei = BigInteger.DivRem(nftsDesired * totalSupply, reserve1, out BigInteger reminder);
if (reminder > 0) amountLPWei += 1;
var amount0 = amountLPWei * reserve0 / totalSupply;
var amount1 = amountLPWei * reserve1 / totalSupply; // should be 1 nft from our LP calculation
// floor amount1 since token1 is nft
var amount1FlooredNoDecimals = amount1.AdjustDecimals(poolData.token1.decimals, 0);
// use helper to account for slippage for amount0 since its an ERC20 token
var amount0Min = TDK.Magicswap.GetAmountMin(amount0, 0.01);
// dont use helper for amount1 since its NFT, use floored value with decimals instead
var amount1Min = amount1FlooredNoDecimals.AdjustDecimals(0, poolData.token1.decimals);
var removeLiquidityBody = new RemoveLiquidityBody {
amountLP = amountLPWei.ToString(),
amount0Min = amount0Min.ToString(),
amount1Min = amount1Min.ToString(),
nfts1 = new List<NFTInput>() {
new() {
id = poolData.token1.collectionTokenIds[0],
quantity = (int)amount1FlooredNoDecimals,
}
}
};
// send request to remove liquidity (ERC20⇔NFT pool in this example)
var transaction = await TDK.Magicswap.RemoveLiquidity(poolData.id, removeLiquidityBody);
// check the result
var success = transaction.status == "mined";
Useful allowance helpers
Some helpers are provided for reading relevant information, such as token balance, allowance, or approval status.
// setup
var poolData = await TDK.Magicswap.GetPoolById(/* your pool id */);
var tokenAddress = TDK.Common.GetContractAddress(poolData.token0.id); // or poolData.token1.id or poolData.id
// getting token balance (ERC20)
var tokenBalance = await TDK.Common.GetFormattedERC20Balance(tokenAddress, TDK.Identity.Address, 18);
Debug.Log($"Balance: {tokenBalance}");
// getting token allowance (ERC20)
var tokenAllowance = await TDK.Magicswap.GetERC20Allowance(tokenAddress, TDK.Identity.Address);
Debug.Log($"Allowance: {Thirdweb.Utils.ToEth(tokenAllowance.ToString())}");
// getting token approval (ERC1155)
var tokenIsApproved = await TDK.Magicswap.IsERC1155Approved(tokenAddress, TDK.Identity.Address);
Debug.Log(tokenIsApproved ? "Token is approved" : "Token is not approved");