Technical Summary
Yama Finance is an omnichain CDP protocol. It is a system of smart contracts that work together to maintain the health of the Yama stablecoin.
In the above diagram, the blue rectangles are core protocol contracts. The yellow rectangles are utility contracts that users can swap out for their own implementations. The red rectangles represent users.
At the top there is the Modular Token smart contract for the stablecoin. It is an ERC20 token that is the core of the protocol. Modular Token has an allowlist. Addresses on the allowlist can mint and burn the token, and they can also modify the allowlist.
Below the Modular Token, we have several contracts that are in the allowlist called modules. One is the CDP Module, which is used by users to borrow the stablecoin against their collateral. Other modules include the Dutch Auction Liquidator, which auctions off collateral from liquidated vaults, and the Bridge Module, which is used to move money between chains. There is also the Balance Sheet Module that keeps track of the protocol deficit/surplus.
The Yama Finance protocol is designed so that users can create a CDP, use it to generate the stablecoin, and then use the stablecoin to purchase goods and services, or to pay for fees. The protocol is also designed to be extensible, so governance can build additional modules on top of it. This allows for the protocol to be adaptable in the long-term.
The CDP Module
The CDP module is a contract which manages the creation and maintenance of collateralized debt pools (CDPs, also called vaults). This is the first way Yama can be minted.
Anyone can create a vault. The CDP module enables users to deposit collateral in these vaults and mint the Yama stablecoin, which is borrowed against the collateral. The collateral is locked in the vault and can only be unlocked by repaying the debt (including interest) with the stablecoin. The debt must not exceed collateralValue * collateralRatio
(where collateralValue
is the value of the collateral based on the price source), or the vault is considered to be undercollateralized and can be liquidated.
If a vault is undercollateralized, the liquidate() function can be called, which calls the liquidator contract (currently the Dutch Auction Liquidator). The liquidator handles the process of the vaults being closed and collateral being sold off to cover the debt. If the collateral is sold for an amount of money more than the debt, the protocol has a deficit (loses money), and if the collateral is sold for an amount of money less than the debt, the protocol has a surplus (gains money).
Users can borrow additional funds, repay the loan, add collateral, or remove collateral from their vaults as long as the following conditions are satisfied:
- The vault does not become undercollateralized
- The vault has not been liquidated
- If borrowing or repaying, the new debt must be either 0 or greater than or equal to the debt floor
- If the user is borrowing, borrowing must be enabled for the collateral type and for the CDP module in general
- If the user is borrowing, the total amount of debt borrowed against the collateral type (for all vaults combined) must not exceed the debt ceiling
- If the user is borrowing and the allowlist is enabled for this collateral type, the user must be on the allowlist (allowlist disabled by default)
All these parameters are set by governance.
The vault of a debt is calculated as debt = vault.initialDebt * collateralType.cumulativeInterest
, where cumulativeInterest
starts at 1 for each collateral type and is updated over time to reflect the interest rate. This means only one number (cumulativeInterest) has to be updated to update the vaults' debt over time.
In other words, updating the debt to reflect interest is an O(1) operation.
Overall, the CDP module provides users with an easy way to borrow the Yama stablecoin by using their mooGLP or other supported tokens as collateral. This allows users to leverage up their positions.
Peg Stability Module (PSM)
Anyone can call deposit() to deposit X amount of USDT into the PSM, and the PSM will mint X amount of Yama and give it to them. Likewise, if the PSM has at least Y amount of USDT, anyone can call withdraw(), which burns Y amount of Yama and transfers Y amount of the external stablecoin to the user.
This ensures the price of Yama can never go above $1. Additionally, if the price of Yama on exchanges drops below $1 and there is USDT in the PSM, arbitrageurs can buy Yama on the exchanges at a lower price and redeem it for USDT. This keeps the price stable.
Governance sets a parameter called the debt ceiling which is the maximum amount of USDT that the PSM contract can hold. If the PSM contract holds more than the debt ceiling, it cannot accept any more deposits.
Flash Mint Module
Anyone can request a flash loan of Yama. The protocol mints X amount of Yama and sends it to the borrower, and the borrower must pay it back in the same transaction, so it is burned.
This helps arbitrageurs, liquidators, etc.
Governance can set a maximum amount of Yama that can be borrowed in a flash loan.
Leverage Proxy
If someone thinks the price of ETH will go up, they can:
- Open an ETH vault using the CDP module, and add some ETH
- Borrow Yama against that ETH
- Convert Yama to USDT using the PSM
- Swap the USDT to ETH using an exchange
- Add more ETH as collateral to the vault
- Repeat steps 2-5 several times
This multiplies their exposure to ETH. However, a simpler way is to:
- Open an ETH vault using the CDP module, and add some ETH
- Use the Flash Mint module to borrow X amount of Yama
- Convert X amount of Yama to USDT using the PSM
- Swap the USDT to ETH using an exchange
- Add more ETH as collateral to the vault
- Borrow X amount of Yama against the new collateral
- Repay the Flash Mint module flash loan with the X amount of Yama borrowed
This way, there is no need to repeat the process. It is simpler and saves gas.
LeverageProxy opens a CDP vault on behalf of the user when LeverageProxy.createVault() is called. If leverageUp() is called, the above process is executed, using a flash loan to leverage up on the collateral asset. leverageDown() does the opposite, using a flash loan to pay off the debt with the collateral.
A formula for calculating maximum leverage for a given collateral ratio is the following, where x is the collateral ratio:
Note that the Leverage Proxy is not on the stablecoin’s allowlist. It cannot mint/burn the stablecoin. It simply acts as a tool to automate the process of leveraging up. Anyone can deploy their own version of the Leverage Proxy and use it to manage their vaults.
PSM lockup
As mentioned in the previous section, people who leverage up on a collateral asset use the PSM to swap their Yama back to the collateral asset. Yama is unique in that it supports very high leverage, so to maintain enough money in the PSM, we incentivize people to lock up their money in the PSM.
By calling lockup() in the PSM lockup, users can deposit USDT for lockup tokens. Lockup tokens accrue value over time as the PSM lockup gets revenue from the protocol. When the user wants to withdraw, they use withdraw() to convert their lockup tokens to Yama.
The Balance Sheet Module
The Balance Sheet module is a contract which keeps track of the protocol's deficit/surplus. Whenever a CDP is liquidated, the Balance Sheet module will update the protocol's deficit/surplus accordingly.
If a Balance Sheet handler is defined, onAddSurplus() and onAddDeficit() are called. At launch, SimpleBSH is the handler and it will transfer surplusAmount * revenueShare / DENOMINATOR
tokens to the PSM lockup whenever a positive surplus is added (and subtract that amount from the total surplus).
The protocol's revenue will come from interest paid by borrowers and any profit made from liquidations.
The Bridge Module
This has not been implemented yet.
The Bridge module is a contract which allows users to move funds between different blockchains. This allows users to move funds from one chain to another, for example Arbitrum to Fuel. This is a useful feature for users that want to transfer funds from one chain to another, or for users that want to take advantage of arbitrage opportunities between different blockchains.
This module burns X amount of Yama on the source chain, and mints X amount of Yama on the destination chain. Hyperlane is used for cross-chain communication.
Additionally, if the receiving address implements the IBridgeReceiver interface, receiver.yamaBridgeCallback() will be called. This allows smart contracts to transfer Yama cross-chain and send information along with the tokens.
Governance can add new chains and change the Hyperlane configuration (i.e. the addresses of the Mailbox, IGP, and ISM contracts).
The Dutch Auction Liquidation Module
The Dutch Auction Liquidation module is a contract that handles collateral auctions. If anyone calls CDPModule.liquidate() on an undercollateralized vault, this module starts a dutch auction of the collateral at price = initialPriceRatio * collateralValue
. Every timeInterval seconds, the price is multiplied by changeRate (causing the price to decrease). At any time, any keeper can claim the collateral in the auction by calling claim(), which burns an amount of Yama equal to the price, and transfers the collateral to the keeper. The assumption here is that as the price drops, it becomes more attractive for keepers to claim.
After the auction is claimed, the protocol incurs a profit equal to auctionPrice - vaultDebt
. If this is negative, the protocol incurs a loss. This is registered in the balance sheet.
If an auction expires (which happens after resetThreshold * timeInterval
seconds), no one can claim the auction and it must be reset.
All these parameters are set by governance. They can be set as defaults or for each collateral type.