Magicswap
This module can be used to get information about Magicswap pools, allowing you to perform token swaps and manage pool liquidity.
Public members
// 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);
Usage
See Assets/Treasure/Example/Scripts/MagicswapUI.cs
for usage examples
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 = await TDK.Common.GetContractAddress(Treasure.Contract.MagicswapV2Router);
// approving ERC20
await TDK.Common.ApproveERC20(Treasure.Contract.Magic, magicswapRouterAddress, amount);
await TDK.Common.ApproveERC20(poolData.id, magicswapRouterAddress, amount);
// approving ERC1155
await TDK.Common.ApproveERC1155(Treasure.Contract.Treasures, magicswapRouterAddress);
Performing a swap
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 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
var userSelectedNFTsOutput = new List<NFTInput> {
new() {
id = routeData.tokenOut.collectionTokenIds[0],
quantity = 1,
}
};
Step 2: Perform the swap
// 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.
- 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
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 Thirdweb getters
The Thirdweb SDK provides some utils for reading relevant information, such as token balance or approval status.
// setup
var poolData = await TDK.Magicswap.GetPoolById(/* your pool id */);
var magicswapRouterAddress = await TDK.Common.GetContractAddress(Treasure.Contract.MagicswapV2Router);
var sdk = TDKServiceLocator.GetService<TDKThirdwebService>().SDK;
var contract = sdk.GetContract(poolData.token0.id); // or poolData.token1.id or poolData.id
// getting token data
var balance = await contract.ERC20.Balance();
var allowance = await contract.ERC20.Allowance(magicswapRouterAddress);
var nftIsApproved = await contract.ERC1155.IsApprovedForAll(TDK.Connect.Address, magicswapRouterAddress);
Debug.Log($"Balance: {Thirdweb.Utils.ToEth(balance.value)}");