Struggling to prepare for a Smart Contract audit?
Check how I prepared for the audit of Gravita protocol
As part of my DeFi protocol by code articles and newsletter I am going to open a section about how do I prepare for the audits.
This way you can both benefit from my research and notes while the audit is ongoing and if you read this after this specific audit has finished you will be able to see an example of how you could do your research in the next bug bounty you participate.
What will you find in this article?
Introduction to Gravita
How does the protocol work?
Important Concepts explained
Which older protocols is it based on?
And which did it forked from?
Changes & New Smart Contracts introduced
How to further prepare for the audit?
Introduction
Gravita Protocol is a decentralised borrowing protocol built on Ethereum that provides users with interest-free loans secured by both Liquid Staking Tokens (LST) and a Stability Pool (SP).
A standard 0,5% borrowing fee is charged upfront on each new debt (or debt increase). The fee is refunded if the user repays his debt before the expiry of six months
Borrowers mint GRAI against the value of their collateral. It can then be used in various ways :
swapping for other assets,
or earning income by providing liquidity
or participating in the stability pool.
How does the protocol work?
To get started users can deposit their collateral into the Gravita protocol and receive GRAI token in return. The amount of GRAI they can borrow depends on the value of their collateral.
Core Smart Contracts involved:
AdminContract.sol
— :
Contains all the functions to create a new collateral or modify their parameters.
It is called by the other contracts to check if a collateral is valid and what are their parameters.
PriceFeed.sol
— Contains functionality for obtaining the current collateral:USD price, which the system uses for calculating collateralization ratios.
Borrowing. No variable fees, only fixed
Many borrowing protocols like Compound and Aave offer a variable borrowing APR that is determined based on supply and demand. With Gravita Protocol you can enjoy interest-free borrowing with a low maximum and fixed one time fee of 0.5% for positions longer than 6 months.
Core Smart Contract involved:
BorrowerOperations.sol
— :
Contains the basic operations by which borrowers interact with their Vessel: Vessel creation, collateral top-up / withdrawal, debt token issuance and repayment.
It also sends issuance fees to the
FeeCollector
contract.BorrowerOperations functions call in to VesselManager, telling it to update Vessel state, where necessary.
BorrowerOperations functions also call in to the various Pools, telling them to move tokens between Pools or between Pool <> user, where necessary.
There are three basic actions users can perform in BorrowerOperations.sol
:
the
openVessel()
method allows them to borrow debt tokens by depositing eligible assets as collateral;then they can either increase their debt position (by borrowing more debt tokens, which may or may not require more deposited collateral) or decrease their debt by repaying debt tokens — both actions are performed by the
adjustVessel()
method;Finally, they can call
closeVessel()
by repaying the debt in full, which will end their loan.
Repaying the debt before six months gets most of the fee refunded
The smart contract controls this decaying refund by immediately remitting the minimum fee (either to the Treasury or to a staking contract) and maintaining a record that includes the refund balance and the timestamp of the last change in the amount from the timestamp of the expiry date.
Core Smart Contract involved:
DebtToken.sol
— The debt token contract, which:
Implements the ERC20 fungible token standard in conjunction with EIP-2612 and a mechanism that blocks (accidental) transfers to addresses like the StabilityPool and address(0) that are not supposed to receive funds through direct transfers.
The contract mints, burns and transfers tokens.
Withdraw from Stability Pool
As a general rule, you can withdraw the deposit made to the Stability Pool at any time. There is no minimum lockup duration. However, withdrawals are temporarily suspended whenever there are liquidatable Vessels with a LTV below the amount set by the protocol that have not been liquidated yet.
Fees
Liquidation fees. In order to reduce the volatility of GRAI, a Liquity-like redemption mechanism will be enabled after launch. Redemptions are made at a rate of 0.97 to 1 (the redeemer pays a 3% fee to the borrower).
The business rules for the FeeCollector.sol
contract are as follows:
a standard 0,5% borrowing fee is charged upfront on each new debt (or debt increase): if Alice borrows $200,000 debt tokens, a $1,000 fee is sent to the fee contract (
increaseDebt(alice, 1000)
), and Alice now owes the platform $201,000the fee is refunded if the user repays his debt before the expiry of six months (~182 days), pro rata for the time elapsed, but the minimum fee corresponds to one week of interest
the contract controls this decaying refund by immediately remitting the minimum fee (either to the Treasury or to a staking contract) and maintaining a record that includes (a) the refund balance (
amount
) (b) the timestamp of the last change in the amount (from
) (c) the timestamp of expiry date (to
): in Alice's case, on day 1, the platform collects 1/26 (one week out of 26) of $1,000, or about $38, and creates a record for her credit of $962, which decays/expires from day 7 to day 182if Alice repays her debt within days 1 through 7, the platform retains the minimum fee and reimburses her for the outstanding balance; if she repays within days 8 through 182, the reimbursement is prorated; after that, she is no longer entitled to reimbursement
each time the methods
increaseDebt()
anddecreaseDebt()
are called, the prorated expired balance is collected by the platform; refunds that have been expired without repayment or expired pro rata fees during the term of the loan may also be collected, but are controlled off-chain
Special cases:
if you make an additional loan before the original refund has expired, all control variables are changed; the new
from
becomes the current timestamp; the newamount
is the unexpired portion of the previous balance plus the newly added fee; the newto
is calculated based on the weight of the addition compared to the original balance and its remaining expiration timeno changes are made to the
from
(starting point for expiration) if additions are made within the first week of lending
Important Concepts explained
What is $GRAI?
$GRAI is an over-collateralized token issued by Gravita Protocol. GRAI can be minted by depositing collateral on Gravita Protocol.
Core Smart Contract involved:
ActivePool.sol
- holds the total balance for each collateral and records the total debt of the active Vessels.
What are Vessels?
A Vessel is where you can deposit collateral against which you can borrow GRAI and maintain your loan. Each Vessel is linked to an Ethereum address and each address can have just one Vessel per collateral type. A Vessel is like a Vault in other platforms.
The maximum amount of GRAI that you can borrow based on your LST collateral of choice.
Core Smart Contracts involved:
VesselManager.sol
and VesselManagerOperations.sol
— :
Contain functionality for liquidations and redemptions.
They send redemption fees to the
FeeCollector
contract.Also contain the state of each Vessel - i.e. a record of the Vessel’s collateral and debt.
VesselManager does not hold value (i.e. tokens).
VesselManager functions call in to the various Pools to tell them to move tokens between Pools, where necessary.
SortedVessels.sol
— :
A doubly linked list that stores addresses of Vessel owners, sorted by their individual collateralization ratio (ICR).
It inserts and re-inserts Vessels at the correct position, based on their ICR.
What is the System Status “Recovery mode”?
Recovery Mode kicks in when the LTV of the collateral type in Gravita Protocol falls below 150%.
During Recovery Mode, Vessels with a collateral ratio below 150% can be liquidated.
Moreover, the system blocks borrower transactions that would further increase the LTV. New GRAI may only be issued by opening a new Vessel with a collateral ratio above 150%.
Core Smart Contract involved:
CollSurplusPool.sol
— :
Holds the collateral surplus from Vessels that have been fully redeemed from as well as from Vessels with an ICR > MCR that were liquidated in Recovery Mode.
Sends the surplus back to the owning borrower, when told to do so by
BorrowerOperations.sol
.
What are liquidations?
To ensure that the entire GRAI supply remains fully backed by collateral, Vessels that fall under the minimum LTV mentioned above, will be closed (liquidated).
The debt of the Vessel is cancelled and absorbed by the Stability Pool and its collateral distributed among Stability Providers.
Anybody can liquidate a Vessel as soon as it drops below the Minimum LTV. The initiator receives a gas compensation (200 GRAI), plus 0.5% of the Vessel’s collateral as reward for this service.
Core Smart Contracts involved:
DefaultPool.sol
— :
holds the total balance for each collateral and records the total debt of the liquidated Vessels that are pending redistribution to active Vessels.
If a Vessel has pending collateral/debt “rewards” in the DefaultPool, then they will be applied to the Vessel when it next undergoes a borrower operation, a redemption, or a liquidation.
GasPool.sol
— :
Holds the total debt token liquidation reserves.
Debt tokens are moved into the
GasPool
when a Vessel is opened, and moved out when a Vessel is liquidated or closed.
What is the Stability Pool?
The Stability Pool is the first line of defence in maintaining system solvency. It achieves that by acting as the source of liquidity to repay debt from liquidated Vessels — ensuring that the total GRAI supply always remains backed.
When any Vessel is liquidated, an amount of GRAI corresponding to the remaining debt of the Vessel is burned from the Stability Pool’s balance to repay its debt. In exchange, the entire collateral from the Vessel is transferred to the Stability Pool.
Core Smart Contract involved:
StabilityPool.sol
— contains functionality for Stability Pool operations:
making deposits, and withdrawing compounded deposits and accumulated collateral and GRVT gains.
Holds the debt token Stability Pool deposits, and the collateral gains for depositors, from liquidations.
Which older protocol is it based on?
Gravita is based on Liquity, which introduced a fully decentralized borrowing protocol for eth.
Liquity had many forks, which expanded on the original design (e.g. allowing multiple ERC-20 tokens as collateral).
Liquity is a decentralized protocol that allows Ether holders to obtain maximum liquidity against their collateral without paying interest. After locking up ETH as collateral in a smart contract and creating an individual position called a “trove”, the user can get instant liquidity by minting LUSD, a USD-pegged stablecoin.
And which protocol did it forked from?
Gravita took inspiration from two in particular:
Vesta is multi-collateral. Each position can have only one collateral type and it is linked to a specific stability pool. Gravita is a fork of Vesta v1.0.
Yeti allows cross-collateral positions, linked to a single stability pool. Gravita uses some of its implementations.
Major changes & New Smart Contracts
Keep an eye on the following smart contracts:
AdminContract
— major rewrite (removed VestaParameters, the collaterals are added and managed from there)
BorrowerOperations
— major refactoring
DebtToken
— added whitelisted burn and mint
FeeCollector
— new contract
PoolBase
— added from Yeti's implementation, to manage multiple collaterals on the StabilityPool
PriceFeed
— major rewrite to add and update the price feed of all collateral types
StabilityPool
— heavy refactoring to have only one StabilityPool linked to all the sorted troves
Timelock
— new contract
VesselManager
— heavy refactoring
VesselManagerOperations
— heavy refactoring. HintHelpers was added here
How to further prepare for the audit?
Getting familiar and understanding all the information mentioned above is going to be of a huge benefit once we start looking at the code, because we are going to be aware of which are the most important Smart Contracts and what we should expect them to do.
Now, besides getting to know this protocol, we need to remember that it is generally based on another protocol, in this case Liquidity protocol. So, the next step is to study and understand how Liquidity protocol works.
Make sure you study and investigate the existing bug bounty reports about Medium and critical issues related to lending and borrowing protocols. An excellent place to do that is https://solodit.xyz/
If you are interested in reading a walk-through Liquidity protocol main smart contracts, so that you get even stronger understanding of Gravita, make sure you subscribe to my newsletter DeFi protocol by code and follow me on Twitter https://twitter.com/TheBlockChainer. Because I will soon be sharing it and you won’t want to miss it.