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:
Field | Type | Description |
---|---|---|
input | uint128 | Quantity of tokens in WAD units to swap in, adding to reserves. |
output | uint128 | Quantity of tokens in WAD units to swap out, removing from reserves. |
useMax | bool | Use true to use the current transient balance. |
poolId | uint64 | Id of the pool to swap in. |
sellAsset | bool | Use 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.
// 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);
}
}