Protocol
Guides
Swapping tokens

Swapping tokens

Overview

Anyone can call the swap function of the Portfolio contract to trade the asset and quote tokens of a non-expired pool. This function expects an Order struct as a parameter:

FieldTypeDescription
inputuint128Quantity of tokens in WAD units to swap in, adding to reserves.
outputuint128Quantity of tokens in WAD units to swap out, removing from reserves.
useMaxboolUse true to use the current transient balance.
poolIduint64Id of the pool to swap in.
sellAssetboolUse true to swap asset for quote tokens, false to swap quote for asset tokens.

The best way to compute the input and output values is to call the getAmountOut function:

function getAmountOut(
    uint64 poolId,
    bool sellAsset,
    uint256 amountIn,
    address swapper
) public view returns (uint256 output);

Most of the time, traders will pay the public swap fee of the pool, however, in the case of a controlled pool, the controller can set a lower swap fee for themselves, giving them an advantage over other traders, especially in the case of arbitraging.

Note: you can read more about arbitraging Portfolio pools here (opens in a new tab).

Potential fail cases

Swapping tokens can fail for a few different reasons:

  • Approvals are not set correctly
  • The target pool doesn't exist or is expired
  • The input and output amounts doesn't satisfy the invariant requirements

Example

Here is a complete Solidity example to swap tokens, however please note that even if using the getAmountOut function is a handy way to get the expected output amount, using it on-chain can lead to sandwich attacks. It is recommended to perform this call off-chain.

SwapExample.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
 
import "portfolio/interfaces/IPortfolio.sol";
import "portfolio/interfaces/IERC20.sol";
 
contract SwapExample {
    IPortfolio public portfolio;
    address public asset;
    address public quote;
 
    constructor(address portfolio_, address asset_, address quote_) {
        portfolio = IPortfolio(portfolio_);
        asset = asset_;
        quote = quote_;
    }
 
    function swap() external {
        // Assuming we want to allocate into the pool `1099511627777`:
        uint64 poolId = 1099511627777;
 
        // Let's fetch our asset balance and use it for the swap.
        uint256 input = IERC20(asset).balanceOf(address(this));
 
        // We approve the Portfolio contract to move our asset tokens
        IERC20(asset).approve(address(portfolio), input);
 
        // Now we check how much quote tokens we can get for our input.
        uint256 output =
            portfolio.getAmountOut(poolId, true, input, 0, address(this));
 
        // Then we prepare our swap order and execute it.
        Order memory order = Order({
            input: uint128(input),
            output: uint128(output),
            useMax: false,
            poolId: poolId,
            sellAsset: true
        });
 
        portfolio.swap(order);
    }
}

© 2023 Primitive™