Surplus Converter - Converting Surplus to sanTokens
Contract used for converting the surplus of the protocol to the desired token

1. Introduction

The SurplusConverter contracts are a set of smart contracts that are used to convert (or swap) the surplus of the protocol in collateral to another token (like for instance the token distributed by the FeeDistributor). Each SurplusConverter can have only one token as output. These contracts then send their output token to another contract, either another SurplusConverter with a different output token or a FeeDistributor in charge of distributing surplus.
There are different types of SurplusConverter contracts corresponding to different types of swaps. They all inherit from the same BaseSurplusConverter contract and implement the same interface.
It is important to note that when handling the conversion of the surplus of the protocol, SurplusConverter contracts can be used one after another. For instance, one could use SurplusConverterUniV3 to swap FEI to USDC and then SurplusConverterSanTokens to convert USDC to sanUSDC_EUR.
In details, so far the protocol has three types of SurplusConverter:
  • SurplusConverterUniV3: Can convert any token on UniswapV3 to the output token supported by the contract
  • SurplusConverterUniV2Sushi: Can convert any token on Sushiswap or UniswapV2 to the output reward token. This contract supports comparing Uniswap and Sushiswap paths and takes the one that is most at its advantage
  • SurplusConverterSanTokens: Can convert a token to a sanToken supported by the Angle Protocol.
Except for the SurplusConverterSanTokens, SurplusConverter contracts will generally involve swaps of large amounts. This means that frontrunners could take advantage of it to do among other things sandwich attacks before each swap. To protect against that, only specific whitelisted addresses are allowed to call SurplusConverter contracts to do swaps.

2. Contract Details

In this section we'll refer to the BaseSurplusConverter contract.


Implements AccessControl, Pausable. It also implements IFeeDistributor which allows SurplusConverter contracts to be used one after another


  • feeDistributor: Address responsible for distributing bought back reward tokens to veANGLE holders or for swapping the reward token of this contract to another token
  • rewardToken: Reward Token obtained by this contract

Access Control

  • GOVERNOR_ROLE: Some functions could be used to withdraw funds, it is important to have a governor in this contract
  • GUARDIAN_ROLE: Role for guardians (and governors) to do rapid updates. For instance, if a whitelisted address is compromised, guardian need to be able to revoke it or to pause the smart contract
  • WHITELISTED_ROLE: For addresses allowed to handle swaps of tokens

3. Key Mechanisms & Concepts

Curve Documentation

The logic of what is implemented here was heavily inspired from Curve does with its fee collection mechanism:
Curve DAO: Fee Collection and Distribution β€” Curve 1.0.0 documentation

Abstract Contract's Functions


function buyback(address token, uint256 amount, uint256 minAmount, bool transfer) external
Buys back rewardToken using the accumulated token and distributes the results of the swaps to the FeeDistributor contract or to the related SurplusConverter contract.
To call this function, you need to be whitelisted because arbitrageurs could take advantage of it to do sandwich attacks. Note that calls to this function could be sandwiched too but it's going harder for miners to setup sandwich attacks.
  • token: Token to use for buybacks of rewardToken. SurplusConverter contracts
  • amount: Amount of tokens to use for the buyback
  • minAmount Specifies the minimum amount to receive, and thus serves as a slippage protection
  • transfer: Whether the function should transfer the bought back rewardToken directly to the feeDistributor contract
The function does different things depending on the SurplusConverter instance.
  • In SurplusConverterSanTokens: it deposits token in Angle and mints a corresponding sanToken. There is no need for a slippage protection in this case
  • In SurplusConverterUniV2Sushi: it compares the Uniswap price with Sushiswap price and performs the swap following the optimal route
  • In SurplusConverterUniV3: it just performs a Uniswap V3 swap
The reason for the variable amount instead of simply using the whole contract's balance for buybacks is that it gives more flexibility to the addresses handling buyback to optimize for the swap prices.


function burn(address token) external
Pulls tokens from another SurplusConverter contract. This function is what allows for composability between different SurplusConverter contracts: a surplus converter having swapped tokens can send its output token to another surplus converter, responsible for doing another type of conversion, by calling this contract.
  • token: Address of the token to pull


function sendToFeeDistributor() external
This function transfers all the accumulated rewardToken to the FeeDistributor contract. The reason for having this function rather than doing such transfers directly in the buyback function is that it can allow to batch transfers and thus optimizes for gas.

Governance Functions

The following functions can only be called by the guardian and/or governor of the contract

pause, unpause

function pause() external
function unpause() external
The two functions above can be called to pause/unpause the buyback, sendToFeeDistributor and burn methods. It can be used in case one of the whitelisted addresses gets corrupted or if feeDistribution has to be stopped for some reason.


function recoverERC20(address tokenAddress, address to, uint256 amount) external
Withdraws ERC20 tokens that could accrue on this contract.
  • tokenAddress: Address of the ERC20 token to withdraw
  • to: Address to transfer to
  • amount: Amount to transfer


function setFeeDistributor(address _feeDistributor) external
Changes the reference to the FeeDistributor allowed to distribute rewards to veANGLE holders or to swap the reward token to another token. Like the recoverERC20 function above, this function is a governor only function as it could technically be used to withdraw funds from the protocol.

Specific Functions from Implementations of the Contract

addToken - SurplusConverterUniV2Sushi

function addToken(address token, address[] memory path, uint8 typePath) external
Adds a token to support with this contract. This function can be called to change the path for a token or to add support for Uniswap or for Sushiswap for a given token. A token could just have one path corresponding to it (like Uniswap), in which case the buyback function does not do any price comparison.
  • token: Token to add to this contract
  • path Path used for the swap
  • typePath: Type of path specified, i.e is it a path for Sushiswap or for Uniswap. typePath = 0 corresponds to a Sushiswap path. typePath = 1 corresponds to a Uniswap path

revokeToken - SurplusConverterUniV2Sushi

function revokeToken(address token, uint8 _type) external
Revokes a supported token by this contract or just a path for this token. You could for instance decide to call this function for a token that supports both Uniswap and Sushiswap paths to revoke the Sushiswap path.
  • token: Token to revoke
  • _type: Type of revokation to make. type = 0 means that just the Sushiswap path needs to be revoked. type = 1 means that just the Uniswap path should be revoked. Other types mean that both Uniswap and Sushiswap paths should be revoked: the token is no longer handled
addToken - SurplusConverterUniV3
function addToken(address token, address[] memory pathAddresses, uint24[] memory pathFees) external
Adds a token to support with this contract. This function can be called to change the path for a token or to add a new supported token.
  • token: Token to add to this contract
  • pathAddresses: Addresses used (in order) for the swap. Note that UniswapV2 interfaces differ from UniswapV3 interfaces.
  • pathFees: Fees used (in order) to get the path for the pool to use for the swap

revokeToken - SurplusConverterUniV3

function revokeToken(address token) external
Revokes a token supported by this contract