PiperX Docs
  • 🌎Welcome
  • 📜Terms of Use
  • Introduction
    • 🎬What is PiperX
    • 📽️What is IPFi
    • 🤖DeFAI
  • User Guide
    • 🏫User Guide One Pager
    • 🌀How to Swap
    • ➕How to Add Liquidity
      • 🎯How to Claim LP Rewards
      • 🆕How to Create a New Pool
      • 👀How to Remove Liquidity
    • 💰How to Farm
  • Developer
    • 📚Smart Contracts
      • Mainnet
      • Aeneid
    • 🧰Integrations (TypeScript)
      • Swap
    • 📗APIs
  • Resources
    • 🔗Links
    • 💽Media Kit
    • ☎️Community Support
    • 🐸OG Program
Powered by GitBook
On this page
  • Integrating Swap to website
  • Steps to Integration
  1. Developer
  2. Integrations (TypeScript)

Swap

Integrating Swap to website

Integrating the PiperX swap functionality to your website makes it easy for you to leverge the liquidity from PiperX without the needs of maintain your own pools. Some example applications including swapping staked $IP tokens to $IP tokens on the LSD website, allow users to buy NFT with stable coins (auto swap stable coins to $IP) on the NFT marketplace.

Steps to Integration

There are three steps in conducting a swap: 1) prepare input, 2) approve tokens, 3) conduct swap. You need to install PiperX sdk and import it to proceed.

0. Prepare Input

You need the following inputs before you can conduct a swap

token1_address: address, // if it is $IP native token, use WIP_ADDRESS
token2_address: address, // if it is $IP native token, use WIP_ADDRESS
amount1: bigint, // amount of token 1 you want to give
amount2Min: bigint, // minimal number of token 2 you want to receive
expire_time: bigint// expiration timestamp for a swap
signer: ethers.Signer. //

Note that, if you are swapping $IP token, you should pass in the PiperX wrapped $IP token address, because the $IP token is not a ERC-20 token. For example, if you are swapping from staked $IP token to $IP token, please use wrapped $IP address WIP_ADDRESS

1. Finding the best swap path

Depends on the swaps that you plan to do, we will use a universal router to find the best swap path, either through standard (V2) or concentrated (V3) path.

import { routingExactInput } from '@piperx/sdk/src/routing'
import { v2_router_abi, piperv3SwapRouter_abi, } from '@piperx/sdk/src/abi'
import { v2RouterAddress, piperv3SwapRouterAddress } from '@piperx/sdk/src/constant'
import { encodeV3Path } from '@piperx/sdk/src/routing'
 
let {bestRoute, max} = routingExactInput(token1_address, token2_address, amount1)

2. Approve tokens

Then you need to approve the router to use your token before swap, depends on the bestRoute that you retreve (it's either a v2 or v3 route), example code are follows

export const routerTokenApproval = async(
    token: string,
    amount: bigint,
    path: string[],
    signer: ethers.Signer
) => {
    if (path[2].length < 10) { // v3 swap
        return await v3RouterTokenApproval(token, amount, signer);
    } else { // v2 swap
        return await v2RouterTokenApproval(token, amount, signer);
    }
}

export const v2RouterTokenApproval = async(
    token: string,
    amount: bigint,
    signer: ethers.Signer
) => {
    try {
        const tokenContract = new ethers.Contract(token, erc20_abi, signer);
    
        const tx = await tokenContract.approve(
            v2RouterAddress, 
            amount
        );
        return tx.wait();
        
    } catch (error) {
        console.error("Error in v2RouterTokenApproval:", error);
        throw error;
    }
}

export const v3RouterTokenApproval = async(
    token: string,
    amount: bigint,
    signer: ethers.Signer
) => {
    try {
        const tokenContract = new ethers.Contract(token, erc20_abi, signer);
        
        const tx = await tokenContract.approve(
            piperv3SwapRouterAddress, 
            amount
        );

        return await tx.wait();
    } catch (error) {
        console.error("Error in v3RouterTokenApproval:", error);
        throw error;
    }
}

3. conduct swap

Depends on the swaps that you plan to do, we will call different functions from the PiperX V2 or V3 contract. One could use the follow universal swap API to deal with different swap cases.

export const swap = async(
    amount1: bigint,
    amount2Min: bigint,
    path: string[], 
    useNative: boolean, // if the WIP address used in the path indicating native token
    expirationTimestamp: bigint,
    signer: ethers.Signer
) => {
    if (path.length > 2 && path[2].length < 10) { // v3 swap
        return await v3Swap(amount1, amount2Min, path, expirationTimestamp, signer);
    } else { // v2 swap
        return await v2Swap(amount1, amount2Min, path, expirationTimestamp, signer);
    }
}

export const v3Swap = async(
    amount1: bigint,
    amount2Min: bigint,
    path: string[], 
    useNative: boolean, // if the WIP address used in the path indicating native token
    expirationTimestamp: bigint,
    signer: ethers.Signer
) => {
 
    const address = await signer.getAddress();
    try {
        const router = new ethers.Contract(piperv3SwapRouterAddress, piperv3SwapRouter_abi, signer);

        let tx;
        
        const txOptions = {
            value: path[0] === WIP_ADDRESS && useNative ? amount1 : 0
        };


        const exactInputParams = {
            path: encodeV3Path(path),
            recipient: path[2] === WIP_ADDRESS && useNative ? ethers.constants.AddressZero : address,
            deadline: expirationTimestamp,
            amountIn: amount1,
            amountOutMinimum: amount2Min
        };

        if (path[0] === WIP_ADDRESS && useNative) { 
            // Case 1: IP to Token (Native IP to ERC-20)
            const swapRouterInterface = new ethers.utils.Interface(piperv3SwapRouter_abi);
            const peripheryPaymentsInterface = new ethers.utils.Interface(IPeripheryPaymentsWithFee_abi);

            const multicallData = [
                swapRouterInterface.encodeFunctionData('exactInput', [exactInputParams]),
                peripheryPaymentsInterface.encodeFunctionData('refundETH')
            ];

            tx = await router.multicall(
                multicallData,
                txOptions
            );
        } else if (path[2] === WIP_ADDRESS && useNative) { 
            // Case 2: Token to IP (ERC-20 to Native IP)
            const swapRouterInterface = new ethers.utils.Interface(piperv3SwapRouter_abi);
            const peripheryPaymentsInterface = new ethers.utils.Interface([
                "function unwrapWETH9(uint256 amountMinimum, address recipient) external payable"
            ]);

            const multicallData = [
                swapRouterInterface.encodeFunctionData('exactInput', [exactInputSingleParams]),
                peripheryPaymentsInterface.encodeFunctionData('unwrapWETH9', [amount2Min, address])
            ];

            tx = await router.multicall(
                multicallData,
                txOptions
            );
        } else { 
            // Case 3: Token to Token
            tx = await router.exactInput(
                exactInputParams,
                txOptions
            );
        }
        
        return await tx.wait();

    } catch (error) {
        console.error("Error in v3 swap:", error);
        throw error;
    }
}

export const v2Swap = async(
    amount1: bigint,
    amount2Min: bigint,
    path: string[], 
    useNative: boolean, // if the WIP address used in the path indicating native token
    expirationTimestamp: bigint,
    signer: ethers.Signer
) => {
    try {
        const router = new ethers.Contract(v2RouterAddress, v2_router_abi, signer);

        let tx;
        if (path[0] == WIP_ADDRESS && useNative) {
            tx = await router.swapExactETHForTokensSupportingFeeOnTransferTokens(
                amount2Min,
                path,
                await signer.getAddress(),  // Make sure we await this
                expirationTimestamp,
                { 
                    value: amount1
                }
            );
        } else if (path[path.length - 1] == WIP_ADDRESS && useNative) {
            tx = await router.swapExactTokensForETHSupportingFeeOnTransferTokens(
                amount1,
                amount2Min,
                path,
                await signer.getAddress(),  // Make sure we await this
                expirationTimestamp
            );
        } else {
            tx = await router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
                amount1,
                amount2Min,
                path,
                await signer.getAddress(),  // Make sure we await this
                expirationTimestamp
            );
        }
        
        return tx.wait()
    } catch (error) {
        console.error("Error in swap:", error);
        throw error;
    }
}
PreviousIntegrations (TypeScript)NextAPIs

Last updated 2 days ago

🧰