Routing

Useful routes to perform protocol operations in a single transaction.

The Router can aggregate operations across ERC4626 protocol vaults, the Principal Token contract and the associated pools (e.g PT-IBT Curve Pools). This gives users access highly-flexible and personalised transactions.

The Router is inspired from the Uniswap UniversalRouter.

How commands work ?

All transactions to the Router go through the execute functions:

  • execute(bytes calldata commands, bytes[] calldata inputs, uint256 deadline)

  • execute(bytes calldata commands, bytes[] calldata inputs)

The first of these functions checks for a deadline parameter. If the block.timestamp is after the deadline provided the transaction will revert. After that check, the 2 functions otherwise execute identically.

The execute functions takes a list of commands , and a list of encoded inputs for each command and execute them in the order specified.

Available Commands

The flexible commands allow users to:

  • Add liquidity in the Curve pools

  • Remove liquidity from the Curve pools

  • Deposit in Principal Tokens

  • Deposit in Interest Bearing Tokens

  • Wrap vault in ERC-4626 Wrapper

  • Redeem from Principal Tokens

  • Redeem from Interest Bearing tokens

  • Unwrap vault from ERC-4626 Wrapper

  • Transfer ERC-20 tokens from Router to any address

  • Transfer ERC-20 tokens from msg.sender to Router

  • Transfer ERC-20 tokens from msg.sender to Router with ERC-2612 permit. Compatible with ERC-4626 IBTs, PTs and YTs.

The list of all the commands along with the accepted parameters can be found in the Router guide.

Some Useful Routes

Add liquidity

Add liquidity with assets

This route exclusively uses underlying tokens as input and carries out all necessary deposits and computations to deposit in the PT-IBT pool at the current spot price of the pool, maintaining the same ratios.

The steps involve:

  1. TRANSFER_FROM: Transfer the underlying tokens from the depositor's wallet to the Router.

  2. DEPOSIT_ASSET_IN_IBT: Deposit all underlying tokens in the specified ERC4626 IBT contract to receive IBT shares.

  3. CURVE_SPLIT_IBT_LIQUIDITY: From the pool's balances, divide the liquidity between the IBT to be added to the pool and the IBT to be deposited in the Principal Token contract in exchange for PT and YT shares. Send the minted YT shares to the user.

  4. CURVE_ADD_LIQUIDITY: Add IBT and PT liquidity to the Curve Pool. Send the Curve LP token to the user.

Example of how to use the router commands to create transactions in Solidity:

import {Commands} from "src/router/Commands.sol";
import {Constants} from "src/router/Constants.sol";

/* 
    ... 
*/

commands = abi.encodePacked(
    bytes1(uint8(Commands.TRANSFER_FROM)),
    bytes1(uint8(Commands.DEPOSIT_ASSET_IN_IBT)),
    bytes1(uint8(Commands.CURVE_SPLIT_IBT_LIQUIDITY)),
    bytes1(uint8(Commands.CURVE_ADD_LIQUIDITY))
);
inputs = new bytes[](4);
inputs[0] = abi.encode(underlying, assets);
inputs[1] = abi.encode(ibt, Constants.CONTRACT_BALANCE, Constants.ADDRESS_THIS);
inputs[2] = abi.encode(
    curvePool,
    Constants.CONTRACT_BALANCE,
    Constants.ADDRESS_THIS,
    receiver
);
inputs[3] = abi.encode(
    curvePool,
    [Constants.CONTRACT_BALANCE, Constants.CONTRACT_BALANCE],
    minMintAmount,
    receiver
);
router.execute(commands, inputs)

Add liquidity with IBTs

This route directly takes IBT shares as input and performs all necessary deposits and computations to deposit in the PT-IBT pool at the current spot price of the pool, maintaining the same ratios.

The steps involve:

  1. TRANSFER_FROM: Transfer the IBTs from the depositor's wallet to the Router.

  2. CURVE_SPLIT_IBT_LIQUIDITY: From the pool's balances, divide the liquidity between the IBT to add to the pool and the IBT to deposit in the Principal Token contract in exchange for PT and YT shares. Send the minted YT shares to the user.

  3. CURVE_ADD_LIQUIDITY: Add IBT and PT liquidity to the Curve Pool. Send the Curve LP token to the user.

Remove liquidity

Remove liquidity for assets

This route withdraws liquidity from Curve, returning the redeemed underlying assets to the user.

The steps involve:

  1. TRANSFER_FROM: Transfer Curve LP tokens from the user's balance to the Router.

  2. TRANSFER_FROM: Transfer the required amount of YT tokens to the Router. This step is necessary only before expiry, as after expiry, redeeming shares from the PT contract does not require the user to burn the YT.

This step is needed before expiry only as after expiry redeeming shares from the PT contract doesn't require the user to burn the YT.

  1. CURVE_REMOVE_LIQUIDITY: Remove liquidity from Curve to retrieve IBT and PT.

  2. REDEEM_PT_FOR_ASSET: Redeem the PT and YT shares from the PT and send assets to the user.

  3. REDEEM_IBT_FOR_ASSET: Redeem the IBT shares from the IBT and send assets to the user.

  4. TRANSFER: Transfer any remaining PTs or YTs if not all PTs and YTs were burnt during the redemption process.

Remove liquidity for IBTs

This route withdraws liquidity from Curve, returning the IBTs to the user.

The steps involve:

  1. TRANSFER_FROM: Transfer Curve LP tokens from the user's balance to the Router.

  2. TRANSFER_FROM: Transfer the required amount of YT tokens to the Router. This step is necessary only before expiry, as after expiry, redeeming shares from the PT contract does not require the user to burn the YT.

This step is needed before expiry only as after expiry redeeming shares from the PT contract doesn't require the user to burn the YT.

  1. CURVE_REMOVE_LIQUIDITY: Remove liquidity from Curve to retrieve IBT and PT.

  2. REDEEM_PT_FOR_IBT: Redeem the PT and YT shares from the PT and send IBTs to the user.

  3. TRANSFER: Transfer any remaining PTs or YTs if not all PTs and YTs were burnt during the redemption process.

Exchange your Yield Tokens

Exchange IBT for YT

This route is used to exchange IBTs for YTs and involves a flash loan of IBT from the Principal Token contract to borrow IBTs.

The steps involve:

  1. FLASH_LOAN: Perform the flash loan in the PT. The flashloan borrower being the Router that will execute the commands an inputs specified in the flash loan. The flashloan hence involves another nested router execution:

    1. DEPOSIT_IBT_IN_PT: Deposit the borrowed IBT in the PT contract

    2. CURVE_SWAP: Swap the received PT from the deposit for IBTs

Note that the Router's onFlashLoan function is responsible for calling back to execute upon receiving flash loan funds. This function is also tasked with returning the borrowed amount along with the flash loan fees, using a safeTransferFrom from the caller's balance.

  1. TRANSFER: Transfer all the YTs to the user

Exchange YT for IBT

This route is used to exchange YTs for IBTs and involves a flash loan of IBT from the Principal Token contract to borrow IBTs.

The steps involve:

  1. FLASH_LOAN: Perform the flash loan in the PT. The flashloan borrower being the Router that will execute the commands an inputs specified in the flash loan. The flashloan hence involves another nested router execution:

    1. CURVE_SWAP: Swap the IBT for PTs to get the same amount of PT than YTs to be able to redeem.

    2. TRANSFER_FROM: Transfer the YTs fro the users balance to the router

    3. REDEEM_PT_FOR_IBT: Redeem PT and YTs for IBTs

Note that the Router's onFlashLoan function is responsible for calling back to execute upon receiving flash loan funds. This function is also tasked with returning the borrowed amount along with the flash loan fees, using a safeTransferFrom from the caller's balance.

  1. TRANSFER: Transfer all the IBTs to the user

Previewing execution

Router also includes a preview functionnality, where a sequence of commands, along with encoded imputs can be simulated to preview the resulting rate, i.e. the amount of output token obtained at the end of execution for each wei of input token spent at the start of execution (multiplied by 1 ray unit).

  • function previewRate( bytes calldata _commands, bytes[] calldata _inputs ) external view returns (uint256)

  • function previewSpotRate( bytes calldata _commands, bytes[] calldata _inputs ) external view returns (uint256)

For ease of use, these functions take similar parameters than the execute() function.

Though previewRate and previewSpotRate mirror input parameters of execute(), some commands are not supported for preview. See the Router guide for more details.

Last updated