SAFE Protection
Building and integrating saviour contracts for SAFEs
1. Overview
The GEB LiquidationEngine allows governance to whitelist external insurance contracts for SAFE
s. SAFE
users can attach 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 saviours) and interfaces can be found here.
2. Contract Interface
Every insurance contract must implement one of the official interfaces (the oldest interface can be found here):
3. Implementation Guidelines
In order to get an idea of how a saviour contract should be implemented and what checks must be in place, let's analyze the components of a demo contract that allows SAFE
users to deposit & withdraw collateral used to save their positions.
Constructor Requirements:
Sanitize every parameter (
address
es must be non null,uint
values are non null and within expected bounds etc)In case of a saviour that adds more collateral in Safes, you must set the CollateralJoin contract of the specific collateral type you're targeting
In case of a saviour that repays debt instead of adding collateral, you must set the CoinJoin contract
Every saviour type should also have the LiquidationEngine, OracleRelayer, SAFEEngine, GebSafeManager, SAFESaviourRegistry and SaviourCRatioSetter set
You must set:
keeperPayout
- amount of collateral awarded to the address that initially called LiquidationEngine.liquidateSAFEminKeeperPayoutValue
- the minimum fiat value of thekeeperPayout
which makes it compelling for keepers to save theSAFE
instead of waiting even more to liquidate itdefaultDesiredCollateralizationRatio
- the default CRatio that aSAFE
will have after it's saved; this CRatio must be greater than the liquidation ratio stored in the OracleRelayer and that is associated withcollateralToken
Optionally, you can set:
payoutToSAFESize
- how many times more collateral there must be in aSAFE
compared tokeeperPayout
; this prevents keepers from purposefully liquidatingSAFE
s so they get a reward that is bigger than the one offered in a collateral auction
When comparing a
liquidationCRatio
from theOracleRelayer
with a desired collateralization ratio, you must first divideliquidationCRatio
byCRATIO_SCALE_DOWN
so you have the same scale for both numbersYou must integrate your saviour with GebSafeManager in order to take advantage of its modularity and friendlier interface compared to core contracts (such as
SAFEEngine
)
Covering & Uncovering SAFEs:
There is no specific way in which users should cover a SAFE
. They can store collateral in the saviour, they can also store aTokens or cTokens that are then used to redeem the underlying assets and add them in the SAFE or they can use a protocol similar to Nexus Mutual which automatically fulfils claims and saves positions. There are, though, certain things that a saviour developer must take into account:
The function used to add more cover for a
SAFE
(like deposit) must revert if the saviour contract is not whitelisted inside the LiquidationEngineUsers can only add cover if their SAFEs have generated debt
Users can withdraw cover (with something like withdraw) even if the saviour contract is not whitelisted inside the LiquidationEngine
Only the
SAFE
's owner or an authorized address inside the GebSafeManager can withdraw cover
Reentrancy
Make sure to protect your cover/uncover functions against re-entrancy.
View Function Requirements:
It must verify if the fiat value of
keeperPayout
exceeds or is equal tominKeeperPayoutValue
It must read the collateral's price from the same oracle used inside
OracleRelayer
in order to maintain consistency between the core system and the saviourIt must return
false
if the oracle price is invalid
It must read the collateral's price from the same oracle used inside
OracleRelayer
in order to maintain consistency between the core system and the saviourIt must return
0
if the oracle price is invalidIt must return the fiat value of
keeperPayout
collateral tokens used to pay keepers for savingSAFE
s
It must return the amount of collateral tokens that will be used to save a
SAFE
and bring its CRatio to the desired ratioIt must read the collateral's price from the same oracle used inside
OracleRelayer
in order to maintain consistency between the core system and the saviourIt must return early if the targeted
SAFE
has no debt or if the oracle feed is invalid
It must return
true
if aSAFE
can currently be saved,false
if notIt must check that, when the
SAFE
is saved, the contract has enough tokens to both reward the keeper that calledLiquidationEngine.liquidateSAFE
and also bring theSAFE
's CRatio to the desired level
Saving a SAFE:
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 saveSAFE
ssaveSAFE
must check thatmsg.sender
is theLiquidationEngine
The
keeper
parameter must not be nullThere is a special condition you must add where, if the
collateralType
is null, thekeeper
is theLiquidationEngine
itself and thesafeHandler
is null, you return a tuple like(true
,uint(-1)
,uint(-1))
. This condition will help theLiquidationEngine
to check that you implementedsaveSAFE
. You can see an example of this condition hereYou must check that
keeperPayoutExceedsMinValue
returnstrue
. If it returnsfalse
, you must return earlyYou must check that the
SAFE
has debt in itYou 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 theSAFE
's debt in case it cannot be saved (its CRatio cannot be increased to the desired level). You must revert in case theSAFE
cannot be savedYou must call
saviourRegistry.markSave(collateralType
,safeHandler)
so that theSAFESaviourRegistry
knows a specificSAFE
has just been saved. The registry enforces a delay between two consecutive save actions for a specificSAFE
. The delay is there to make sure thatSAFE
users don't solely rely on saviours to protect their positions. This way we avoid a scenario where one or a couple of popular saviours fail (e.g bugs, lack of sufficient cover) and most positions in the system are liquidated at onceYou must emit a
SaveSAFE
event before you returnThe 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 theSAFE
keeperPayout
is a non null amount of collateral that was used to reward the keeper
You can check an example implementation for saveSAFE
here.
4. Monetizing a Saviour
In some cases, saviour 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 saviour, you must keep in mind several things:
Monetization should happen outside of the saviour contract. This keeps concerns separated, simplifies the saviour implementation and gives you more flexibility when it comes to updating your business model
You must mention that your saviour 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 belowAssuming you plan to use a smart contract to charge users, your should attach a detailed overview or implementation of your model inside your GIP
5. Launching on Mainnet
In order to launch and integrate a saviour with a mainnet deployed GEB, the saviour must first pass several checks:
You must first create a new GEB Improvement Proposal. Once you create the GIP you must ask for feedback on Reflexer's Discord server (in the development channel). To maximize your chances of having your idea accepted:
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
andpayoutToSAFESize
values you plan to setYou must specify if you plan to monetize the saviour service you're building and how you plan to do it
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 this one) that will be used by others to connect your saviour to Safes and also add cover.
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 forkeeperPayout
,minKeeperPayoutValue
,payoutToSAFESize
anddefaultDesiredCollateralizationRatio
. 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 savedAssuming that you pass all previous steps, you can deploy your saviour on mainnet so that governance can whitelist it in
LiquidationEngine
and inSAFESaviourRegistry
6. Integration Ideas
To help you get started, here are a couple of ideas for building RAI saviours:
Allow users to deposit Aave aETH in a saviour which can then be used to redeem ETH that is then added in a
SAFE
Allow users to deposit Compound cETH in a saviour which can then be used to redeem ETH
that is then added in a
SAFE
Last updated