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 thereceiver
depositFee
- how much is going to be subtracted fromassets
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 fromreceiver
to the portfoliomintFee
- how many assets are going to be sent fromreceiver
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 assetsmaxDeposit(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 theowner
withdrawFee
- how much is going to be withdrawn from the vault on top of theassets
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 thereceiver
mintFee
- how many assets are going to be taken from the vault on top of theassets
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 assetsmaxWithdraw(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 redeemmaxRedeem(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.
Last updated