SAFE Protection
Building and integrating saviour contracts for SAFEs
Last updated
Building and integrating saviour contracts for SAFEs
Last updated
The GEB allows governance to external insurance contracts for SAFE
s. SAFE
users can insurance contracts to their positions and this way have an extra layer of protection against liquidation.
Anyone can build and propose new insurance contracts, assuming that the contracts abide by the requirements and principles outlined below. A central repository with SAFE
insurance contracts (also called saviors) and interfaces can be found .
Every insurance contract must implement one of the official interfaces (the oldest interface can be found ):
Sanitize every parameter (address
es must be non-null, uint
values are non-null and within expected bounds etc)
You must set:
minKeeperPayoutValue
- the minimum fiat value of the keeperPayout
which makes it compelling for keepers to save the SAFE
instead of waiting even more to liquidate it
Optionally, you can set:
payoutToSAFESize
- how many times more collateral there must be in a SAFE
compared to keeperPayout
; this prevents keepers from purposefully liquidating SAFE
s so they get a reward that is bigger than the one offered in a collateral auction
When comparing a liquidationCRatio
from the OracleRelayer
with a desired collateralization ratio, you must first divide liquidationCRatio
by CRATIO_SCALE_DOWN
so you have the same scale for both numbers
Reentrancy
Make sure to protect your cover/uncover functions against re-entrancy.
It must verify if the fiat value of keeperPayout
exceeds or is equal to minKeeperPayoutValue
It must return false
if the oracle price is invalid
It must return 0
if the oracle price is invalid
It must return the fiat value of keeperPayout
collateral tokens used to pay keepers for saving SAFE
s
It must return the amount of collateral tokens that will be used to save a SAFE
and bring its CRatio to the desired ratio
It must return early if the targeted SAFE
has no debt or if the oracle feed is invalid
It must return true
if a SAFE
can currently be saved, false
if not
It must check that, when the SAFE
is saved, the contract has enough tokens to both reward the keeper that called LiquidationEngine.liquidateSAFE
and also bring the SAFE
's CRatio to the desired level
The process of saving a SAFE
has its own requirements:
You must implement and use saveSAFE(address keeper
, bytes32 collateralType
, address safeHandler) external returns (bool
, uint256
, uint256)
in order to save SAFE
s
saveSAFE
must check that msg.sender
is the LiquidationEngine
The keeper
parameter must not be null
You must check that keeperPayoutExceedsMinValue
returns true
. If it returns false
, you must return early
You must check that the SAFE
has debt in it
You must check that the saviour can both reward the keeper for saving the SAFE and also add enough collateral in the SAFE so its CRatio goes to the desired level
You must not add any collateral in the SAFE
or repay the SAFE
's debt in case it cannot be saved (its CRatio cannot be increased to the desired level). You must revert in case the SAFE
cannot be saved
You must call saviourRegistry.markSave(collateralType
, safeHandler)
so that the SAFESaviourRegistry
knows a specific SAFE
has just been saved. The registry enforces a delay between two consecutive save actions for a specific SAFE
. The delay is there to make sure that SAFE
users don't solely rely on saviors to protect their positions. This way we avoid a scenario where one or a couple of popular saviors fail (e.g bugs, lack of sufficient cover) and most positions in the system are liquidated at once
You must emit a SaveSAFE
event before you return
The last thing you have to do is to return a tuple in the form of (true
, tokenAmountUsed
, keeperPayout)
where:
tokenAmountUsed
is the amount of collateral that was used to save the SAFE
keeperPayout
is a non-null amount of collateral that was used to reward the keeper
In some cases, savior builders may want to monetize the service they provide and charge some sort of fee for protecting SAFE
s.
If you would like to submit a proposal for implementing a monetized savior, you must keep in mind several things:
Monetization should happen outside of the savior contract. This keeps concerns separated, simplifies the savior implementation and gives you more flexibility when it comes to updating your business model
You must mention that your savior will be monetized and you should give a detailed description of how you plan to charge SAFE
users. You must include the description in your GIP as outlined in the section below
Assuming you plan to use a smart contract to charge users, you should attach a detailed overview or implementation of your model inside your GIP
In order to launch and integrate a saviour with a mainnet deployed GEB, the saviour must first pass several checks:
Your saviour must only do one thing. For example, you should only handle aTokens or cTokens, not both
Your saviour should only take into account a single collateral type (e.g ETH-A or ETH-B, not ETH-A and ETH-B) in case it's meant to add collateral. If the saviour repays debt, it can be generalized to handle any Safe with any collateral type
You should have a draft implementation of your saviour with estimated gas amounts for calling each function
You should give an initial estimate of the keeperPayout
, minKeeperPayoutValue
and payoutToSAFESize
values you plan to set
You must specify if you plan to monetize the saviour service you're building and how you plan to do it
Before you submit your full implementation and update the GIP, you must make sure that you have 100% test coverage for your code and also do several integration tests between the LiquidationEngine
, SAFESaviourRegistry
and the saviour code. In order to submit your implementation, update your GIP with a link to your code and a new summary of the gas amounts required to call each function, as well as updated values for keeperPayout
, minKeeperPayoutValue
, payoutToSAFESize
and defaultDesiredCollateralizationRatio
. After you update the GIP, ping the community on Discord.
Once your implementation is accepted and reviewed by the community, your code must also be audited twice. Each audit must be done by an independent party.
After your code gets audited, you should send a new message on Discord and let the community know that it's ready to be integrated in production. You must link to the audit reports.
Governance may decide to first try out your saviour on a testnet. In this case, you must deploy an instance of your saviour on a testnet GEB and liquidate a SAFE
which can then be saved
Assuming that you pass all previous steps, you can deploy your saviour on mainnet so that governance can whitelist it in LiquidationEngine
and in SAFESaviourRegistry
To help you get started, here are a couple of ideas for building RAI saviours:
that is then added in a SAFE
In order to get an idea of how a savior contract should be implemented and what checks must be in place, let's analyze the components of a that allows SAFE
users to deposit & withdraw collateral used to save their positions.
In case of a saviour that adds more collateral in Safes, you must set the contract of the specific collateral type you're targeting
In case of a savior that repays debt instead of adding collateral, you must set the contract
Every savior type should also have the , , , , and set
keeperPayout
- amount of collateral awarded to the address that was initially called
defaultDesiredCollateralizationRatio
- the default CRatio that a SAFE
will have after it's saved; this CRatio must be greater than the liquidation ratio stored in the and that is associated with collateralToken
You must integrate your savior with in order to take advantage of its modularity and friendlier interface compared to core contracts (such as SAFEEngine
)
There is no specific way in which users should cover a SAFE
. They can store collateral in the savior, they can also store or that are then used to redeem the underlying assets and add them in the SAFE or they can use a protocol similar to which automatically fulfills claims and saves positions. There are, though, certain things that a savior developer must take into account:
The function used to add more cover for a SAFE
(like ) must revert if the savior contract is not whitelisted inside the
Users can only add cover if their SAFEs
Users can withdraw cover (with something like ) even if the savior contract is not whitelisted inside the
Only the SAFE
's owner or an authorized address inside the can withdraw cover
:
It must read the collateral's price from the used inside OracleRelayer
in order to maintain consistency between the core system and the savior
:
It must read the collateral's price from the used inside OracleRelayer
in order to maintain consistency between the core system and the savior
:
It must read the collateral's price from the used inside OracleRelayer
in order to maintain consistency between the core system and the savior
:
There is a special condition you must add where, if the collateralType
is null, the keeper
is the LiquidationEngine
itself and the safeHandler
is null, you return a tuple like (true
, uint(-1)
, uint(-1))
. This condition will help the LiquidationEngine
to check that you implemented saveSAFE
. You can see an example of this condition
You can check an example implementation for saveSAFE
.
You must first create a new . Once you create the GIP you must ask for feedback on (in the development channel). To maximize your chances of having your idea accepted:
Once you receive feedback (and assuming it's positive), you can start to fully implement the saviour. Keep in mind that, aside from the saviour, you will need to create a proxy actions contract (like ) that will be used by others to connect your saviour to Safes and also add cover.
Allow users to deposit in a saviour which can then be used to redeem ETH that is then added in a SAFE
Allow users to deposit in a saviour which can then be used to redeem ETH
or options which can be exercised when saveSAFE
is called