TrueFi Docs
Search
K
Comment on page

Controllers

🚀 Intro

Controllers regulate different aspects of how TrueFi products work.
They are responsible for key decision-making on how to perform most of the lender-facing operations. There are three Controllers that TrueFi utilizes:
  • Transfer Controller
  • Deposit Controller
  • Withdrawal Controller
They can implement any logic and freely interact with any external on-chain or off-chain systems.
The details of how these controller work, what are they exactly responsible for and how Vaults interact with them is located below.

How to read interface notation

whenThisFunctionIsCalledOnTheVault(arguments)
vaultCallsThisFunctionOnTheController(msg.sender, arguments)
Returns:
  • controllersResponse - description of how the Vault will handle the Controller’s response
Parameters of the Vault’s methods are described in the ERC-4626 documentation.

🔁 Transfer Controller

Transfer Controller is responsible for setting restrictions around transfers of the share token (LP token). Transfer controller has to be capable of making a binary decision whether a particular transfer is allowed or not.

Interface

transfer(to, value)
transferFrom(from, to, value)
onTransfer(msg.sender, from, to, value)
Returns:
isTransferAllowed - boolean determining whether a particular transfer is allowed or not (vault will execute the transfer on true and block the revert the transfer on false)

Examples

Examples of the most common Transfer Controllers are:
  • open - all transfers are allowed
  • blocked - all transfers are forbidden and only minting or burning the token is allowed
  • restricted - only transfers to farming contract (the one distributing incentives) are allowed

⤵️ Deposit Controller

Deposit Controller is responsible for handling all deposits. On a high level it can set:
  • Lender restrictions (who can deposit into the Vault and when)
  • LP token price at the deposit
  • Deposit fee paid to the Portfolio Manager
  • Maximum amount that can be deposited
  • Potentially many more via implicit manipulation of the response parameters…

Interface

deposit(assets, receiver)
onDeposit(msg.sender, assets, receiver)
Returns:
  • shares - how many shares are gonna be minted to the receiver
  • depositFee - how much is going to be subtracted from assets and sent to the manager beneficiary address
mint(shares, receiver)
onMint(msg.sender, shares, receiver)
Returns:
  • assets - how many assets are going to be sent from receiver to the portfolio
  • mintFee - how many assets are going to be sent from receiver to the manager fee beneficiary
Additionally, Deposit Controller needs to handle all the necessary view functions. The Vault calls these functions on the Controller, when they are called on the Vault. Their interfaces are identical to the original ERC-4626 interfaces.
  • previewDeposit(assets) - returns how many LP will the depositor get for the assets
  • maxDeposit(receiver) - returns max amount of assets that can be put into deposit (before fee subtraction)
  • previewMint(shares) - returns how many assets does user need to send to mint particular number of shares (actually deposited + fee)
  • maxMint(receiver) - returns max amount of shares that can be minted

Examples

Examples of the most common Deposit Controllers are:
  • Simple - deposits are always allowed, setting deposit ceiling
  • Simple with lender restrictions - deposits are allowed for KYCed users, setting deposit ceiling
  • Almighty - Portfolio Manager can set custom deposit parameters for each user (LP token price, fee, deposit limit)

⤴️ Withdrawal Controller

Withdrawal Controller is responsible for handling all withdrawals. It can set:
  • Who and when can withdraw the Vault
  • LP token price at the withdrawal
  • Withdrawal fee paid to the Portfolio Manager
  • Maximum amount that can be withdrawn
  • Potentially many more via implicit manipulation of the response parameters…

Interface

withdraw(assets, receiver, owner)
onWithdraw(msg.sender, assets, receiver, owner)
Returns:
  • shares - how many shares are gonna be burned from the owner
  • withdrawFee - how much is going to be withdrawn from the vault on top of the assets and sent to the manager beneficiary address
**redeem(shares, receiver, owner)**
onRedeem(msg.sender, shares, receiver, owner)
Returns:
  • assets - how many assets are going to be sent from the vault to the receiver
  • mintFee - how many assets are going to be taken from the vault on top of the assets and sent to the manager fee beneficiary
Additionally, Withdrawal Controller handles necessary view functions. Their interfaces are identical to the original ERC-4626 interfaces.
  • previewWithdraw(assets) - returns how many LP will one need to burn in order to get desired amount of assets
  • maxWithdraw(owner) - returns max amount of assets that can be withdrawn (after paying fee)
  • previewRedeem(shares) - returns how many assets will user get (after paying fee) for a particular redeem
  • maxRedeem(receiver) - returns max amount of shares that can be burned to redeem

Examples

Examples of the most common Withdrawal Controllers are:
  • Simple - withdrawals are always allowed, setting withdrawal floor
  • Almighty - Portfolio Manager can set custom withdrawal parameters for each user (LP token price, fee, deposit limit)

💡 General Guidelines

👮‍♂️ Controller Whitelist

Controllers are approved by DAO Governance. Only approved Controllers are available in the TrueFi interface. Any new Controller proposal should consist not only of smart contract, but also a json file that would configure the controller UI in Portfolio Manager settings, a UI of actions on redeem / deposit button and so on.

Controller Whitelisting Request

A Controller Whitelisting Request should include
  • All on-chain addresses necessary to properly integrate the Controller
  • Controller source code
  • UI JSON file
    • List of fields and options for controller column in the UI
    • Actions to execute upon click on Lender’s UI elements

🌗 Portfolio Phases Support

It’s important for Deposit and Withdrawal Controllers to support Portfolio lifecycle phases. Behavior of the Controller should match the intentions expressed in the documentation of a particular TrueFi product, that the Controller is aiming to serve.
Examples:
  • A good and predictable Withdrawal Controller would remove all restrictions for withdrawing when the Portfolio goes into Closed state.
  • A good and predictable Deposit Controller would ban all deposits when the Portfolio goes into Closed state

🗂 State Management

Controllers typically don’t hold any state, so their deployment doesn’t require a separate proxy contract for storage. The easiest and the fastest way to onboard a controller is the one that doesn’t hold any vault-specific state. It can store a general state and delegate decisions to other, state-holding contracts. Generally, Controllers are free to delegate any logic to other, external contracts.
The process of deploying state-holding controllers is a more complicated, as it requires the deployment of a proxy, but it is still possible and if there is a need to do so, builders are welcome and encouraged to do so.

Lender restrictions

Lender restrictions could be built within the Controller, but it’s more common for logic on lender restricitions to be delegated outside of the Controller.
A Controller can utilize another contract to check whether a user is allowed to interact with the Vault by checking an allowlist, NFT ownership, SBT ownership, etc.