B.Protocol v2 Integration : technical review

The B.Protocol introduces a novel way of handling liquidations. Instead of auctioning collateral in the open market, which exposes keepers to high fees and MEV, the B.Protocol settles the liquidation immediately using liquidity from a liquidity pool. This way, keepers become liquidity providers, which lowers their costs and risks, increases their profits, lowers the barrier of entry for new players, and improves the overall efficiency for liquidations.

About this document

This document was prepared by the Protocol Engineering Core Unit. It provides a technical review of B.Protocol v2 as it was in commit 477b5001d35d351ed8a5acd993341981480048a0 from the mip branch of the following Git repository: GitHub - backstop-protocol/Maker-Backstop.

It is intended for all audiences who have a technical understanding of how Maker works. Since the code reviewed is more of a “reference for possible B.Protocol functionality” than an actual final implementation, this review focuses on the conceptual aspect of the code and provides high-level recommendations.

A code audit of the final implementation will still be needed.

About the B.Protocol

The B.Protocol team is composed of Yaron Velner, CEO & Protocol Architect, Eitan Katchka, Head of Ecosystem Development, and Shmuel Disraeli, Full Stack Engineer. A year ago they released the first version of the B.Protocol which went live on October 27th, 2020.

One month after going live, Yaron Velner wrote a post on this forum proposing an integration with Maker. Although it had a good reception, Yaron decided to withdraw his declaration of intent a few months later as the team was working on a new version of their product.

B.Protocol v2, also known as B.AMM, was introduced last june and a few weeks later it went live as a component of the Liquity protocol. This integration with Liquity was audited and is currently the object of a bounty program on bug bounty platform ImmuneFi.

Three days after introducing B.AMM, Yaron resubmitted his declaration of intent on this forum. Last August, Yaron presented it as part of Maker’s G&R call 153, and soon afterwards it passed a ratification poll. Finally, on October 17th, Yaron submitted a proof-of-concept implementation of B.AMM’s integration with Maker.

How it works

Before addressing the specific mechanics of the B.AMM, let us recall the way Maker liquidations are currently triggered.

How Maker vaults get liquidated

Anybody can trigger a liquidation by calling bark in the Dog contract and specifying a vault. If that vault is unsafe, the Dog removes it from the system by transferring its collateral to a Clipper contract. There are many Clippers, one per collateral type.

Then, the Dog calls the Clipper’s kick function, which sets an initial price for the collateral, and starts the public auction.

The B.AMM’s proposal is to change some of our Clippers into their Blippers, leaving the Dog’s functionality intact.

How Blippers work

Once connected to the system, Blippers receive collateral from the Dog. Instead of starting a public auction like the Clippers do, Blippers attempt to settle the liquidation immediately by using an Automated Market Maker (AMM). AMMs are smart contracts that hold two or more assets and specify a trading strategy between them. In this case, these assets are Dai and the collateral being liquidated. Thus, there is one liquidity pool per collateral.

In order to determine the price of the collateral being liquidated, Blippers use the Medianizer instead of the OSM. This allows them to mimic the behavior of keepers by being able to obtain the price in real time. It comes, however, at the expense of being more vulnerable to oracle attacks, which is why Blippers revert if their execution results in bad debt.

If a Blipper fails to liquidate a vault for lack of liquidity or some other reason, it will fall back to the Clipper functionality and start a public auction. That is why liquidity pools are at the heart of B.AMM.

B.AMM’s liquidity pools

B.AMM’s liquidity pools’ incentivization mechanisms differ slightly from other liquidity pools’. They need to have a lot of DAI liquidity to account for sudden and unexpected liquidations of large amounts of Maker collateral. So in order to achieve such large amounts of DAI liquidity, they need to incentivize liquidity providers to deposit it.

Liquidity providers can deposit and withdraw liquidity from the pool. When they withdraw, they get a balanced amount of both Dai and collateral ; when depositing, however, they can only provide DAI.

Since the pool charges a fee every time it receives collateral from Maker, liquidity providers obtain a benefit when they withdraw, proportional to the amount of liquidated collateral they helped providing liquidity for.

Using Compound to store the pool’s DAI

These pools maintain their assets in two differet places. For their collateral side where they store Maker’s liquidated assets, they store it as internal gem. In other words, they keep it inside of Maker.

Not so for the DAI side of the pool, which is stored in Compound. Although this undoubtedly helps motivate liquidity providers, it also adds systemic risk to Maker. Yaron is open to using the DSR instead, which is a far safer alternative.

Buying collateral tokens from a pool

Pools have a swap function that allows users to exchange DAI for collateral. This functionality is somewhat similar to what Maker auctions currently do, in the sense that they allow the general public to buy tokens at a certain discount and then sell them in the open market for a marginal profit.

This swap function plays an essential role in rebalancing the pool. It uses Maker’s oracles to obtain the price of the pool’s collateral tokens and then a StableSwap algorithm measures how far the pool is from a balance. The farther it is, the higher the discount it provides.

This means that B.AMM acts as a buffer between Maker’s liquidations and keepers. This is highly advanteageous to both Maker and keepers. Maker obtains instant liquidations and diminishes its solvency risk during sharp market declines ; keepers benefit by not having to participate in highly competitive and time-sensitive auctions, which reduces their risks, makes their business more predictable, and lowers their infrastructure costs.

The B.Protocol charges a fee for every swap made through their AMM.

How much DAI does Maker get back

While a regular Clipper uses auctions to determine the price of the collateral it is liquidating, B.AMM settles liquidations instantly by transferring a certain amount of DAI back to the protocol. How is this amount obtained ?

First, let us recall how Maker vaults get liquidated. When the Dog removes an unsafe vault from the system, it registers the vault’s debt as bad debt owed by the Vow contract. That is why Clippers move the DAI raised back to the Vow.

With Blippers, then, the question is : how much DAI do they move back to the Vow after a successful liquidation ? This is very important because it allows us to examine Maker’s profitability from this new system.

Obtaining owe

In the context of a Blipper contract, owe is the amount of DAI the Blipper moves to the Vow when it liquidates collateral. The following Solidity code shows how it is obtained. It was slightly simplified from the original in order to abstract away safe math operations and fixed-point arithmetic:

1    uint256 ask = tab / (mid / bee);
2    uint256 bid = lot * mid * bee;
3    if(ask <= lot) {
4        owe = tab;
5    }
6    else {
7        owe = bid;
8        require(owe * chop >= tab, "Blipper/low-ink");
9    }

Here is the meaning of each variable:

  • lot: amount of collateral available
  • tab: amount of DAI the system expects (vault’s debt + penalty)
  • owe: amount of DAI the system will get
  • mid: real-time price of collateral
  • bee: B.AMM discount
  • chop: liquidation penalty

Under normal circumstances, line 4 above will be executed and the system will get back the amount of DAI it expects. Under stressful conditions, however, the system might get less. In a really bad scenario, B.AMM will not liquidate the vault at all and it will fall back to a Clipper auction instead. What are these scenarios ? It depends on wether a mistake was made in the bid calculation above.

A potential mistake in the bid calculation

Line 2 above might make more sense as uint256 bid = lot * mid / bee. Otherwise, bid will be higher than tab on some stressful scenarios. The explanation is that lot * mid is the value of the collateral at current prices ; if it is multiplied by bee, it will become higher, since bee is of the form 1 + fraction.

Response from the B.Protocol

The B.Protocol acknowledged this and fixed it in a pull request against the branch that we reviewed.

Plotting owe as a function of mid

In order to understand the behavior of owe under different market conditions, we plotted it against mid, the real-time price of collateral:

The X axis represents the price of collateral, while the Y axis represents the amount the system gets in return. The blue line represents how the system behaves under normal conditions, and the other two lines represent its behavior under market pressure. The red line is how it is currently implemented [this was fixed], and the green line is our proposal for fixing the bid calculation. You can see the formulas and constants, and play with them here.

Note that, under a certain price threshold, the line stops. This represents the moment at which B.AMM no longer liquidates the collateral due to its low price and reverts back to a Clipper auction.

Should Blippers revert if the price is too low ?

If we remove the low-ink require from line 8 above, Blippers would not revert if the price is too low. Instead, they would move an insufficient amount of DAI to the Vow, creating bad debt in the process as they would be unable to fully repay the liquidated Vault’s debt.

However, an auction under these circumstances would probably not yield better results if the price is correct. As auctions take much longer, the resulting DAI could be significantly lower.

On the other hand, if the price is wrong, an auction could be more beneficial to the system. If, for instance, oracles are under attack, an auction would benefit from the extra layer of safety offered by the OSM.

The Risk team can provide very valuable input on this decision.

Is there any risk of auction grinding ?

Auction grinding is a type of attack on Maker liquidations. It takes place when the owner of a vault that is being liquidated participates in the auction. Under some circumstances, the owner can make a profit out of the liquidation and end up with a more valuable portfolio than if they had paid their debt back.

For the case of a B.AMM-backed collateral type, there is no auction that the vault owner can participate in, but they can swap the liquidated collateral to Dai for a discount. Are there any circumstances under which this can be profitable to a vault owner who liquidates their vault on purpose ?

B.AMM uses the StableSwap algorithm to determine the discount offered on the swap trade. This means that the discount will increment in an inversely proportional way to the amount of DAI liquidity available in the pool.

Assuming an attacker has the means to create an arbitrarily large vault, they can operate as follows : If they create a vault that costs more to liquidate than the available liquidity in the pool, B.AMM will refuse to liquidate it so it will fallback to a regular auction.

However, the attacker can craft a vault big enough to be barely liquidatable through B.AMM, using all its available liquidity. This means that the B.AMM pool will be left with a large amount of collateral and no DAI liquidity.

Then, the attacker can buy the collateral using the swap function. Since the available liquidity affects the price calculation, and B.AMM is in a state where it desperately needs liquidity, the attacker can get the collateral for a very low DAI price.

In order to prevent this, however, B.AMM has a maxDiscount parameter that allows it to control the maximum discount a buyer of collateral can obtain. As long as this parameter is set correctly, a grinding attack is impossible. What values are safe for this parameter ?

Obtaining safe values for maxDiscount

Let us assume an attacker sets up a vault such that its liquidation will drain all the liquidity from the B.AMM. They then trigger a liquidation, and immediately thereafter buy all the collateral that was liquidated, such that they end up with the exact same amount of collateral they started with.

We need to compute the profit the attacker obtains from this operation. Since their amount of collateral does not change and they started with zero DAI, their profit consists of the amount of DAI they have at the end of the attack.

The attacker has two DAI flows : first, get DAI from vault debt, and then pay DAI to purchase the liquidated collateral :

profit = debt - purchase

Since we are assuming all the collateral is bought in a liquidity worst-case scenario, the purchase amount will be the lowest possible, and it will be constrained by maxDiscount :

profit = debt - (collateral * price / maxDiscount)

What is the amount of collateral that was liquidated ? Assuming normal market conditions, it is computed in the Blipper as the expected liquidation amount with the B.AMM fee, divided by the price of the collateral :

profit = debt - (tab * bee / price) * price / maxDiscount
profit = debt - tab * bee / maxDiscount

Now, the expected liquidation amount tab is calculated in the Dog as the vault’s debt with the liquidation penalty :

profit = debt - (debt * chop) * bee / maxDiscount

Since we want the attacker to lose money, we need profit < 0 :

debt - debt * chop * bee / maxDiscount < 0

Solving for maxDiscount :

debt < debt * chop * bee / maxDiscount
debt * maxDiscount < debt * chop * bee
maxDiscount < chop * bee

Thus, in order to prevent a grinding attack, Governance must ensure that the B.AMM’s maximum discount on swap operations never exceeds the product of the liquidation penalty and B.AMM’s liquidation fee.

The B.Protocol is planning to always have maxDiscount < bee in order to prevent these attacks.

Interacting with the B.Protocol

At a technical level

As it is still in an early stage, the B.AMM cannot yet operate on its own and needs to be tightly integrated with each protocol it interacts with. This means that, for every protocol integration, the B.Protocol team needs to create a new version of B.AMM, tailored specifically for each project’s needs.

This means that its code has not been tested in production.

The Maker version of B.AMM consists mainly of two contracts: Blipper and bammJoin. The former inherits from Maker’s Clipper contract and needs to be relied in the Vat ; the latter implements a B.AMM liquidity pool and does not need any special permissions. Both contracts are per-collateral and will need to be deployed multiple times if Maker intends to integrate more than one collateral with B.Protocol.

For a B.AMM-backed collateral, we think it is better to deploy a Blipper and a Clipper as separate contracts. This will make it easier to update either one of them. Thus, instead of inheriting from a Clipper, Blipper contracts should store their associated Clipper address. As they still need to access some of the Clipper parameters, they can use a synchronization mechanism to save gas. If they need to fall back to the Clipper, they should transfer the collateral to it and then make an external call to its kick function.

Response from the B.Protocol

The B.Protocol addressed this in a pull request against the branch we reviewed.

At a human level

It should be made clear from the beginning what party is responsible for maintaining the Maker version of B.AMM at the smart contract level.

Since the Blipper contracts need to be relied in the Vat, we propose that they should be considered as core contracts and thus be maintained, audited and deployed by the PECU or other MakerDAO Core Unit. We also propose that the Blipper source code should be stored in MakerDAO’s DSS repository along with other core contracts.

bammJoin contracts, on the other hand, don’t need to be relied in the Vat and can be onboarded and offboarded by setting their addresses in the Blippers. Thus, we propose that the responsiblity for maintaining, auditing and deploying them belong to the B.Protocol.

A conversation between both technical teams should take place in order to reach an agreement on these responsibilities.

Summary of recommendations

13 Likes

I’m happy to see B.Protocol’s contribution and believe that they should be compensated, but what service, going forward, does B.Protocol provide? I mean, besides basic respect, what prevents Maker from taking the code, removing the extra fee sent to B.Protocol, and deploying it?

1 Like

This is a great question.

The proposed system is more than just code, and heavily relies on sufficient user deposits. Obtaining user deposits requires both community effort and development effort.

From the community side we are building backstops for other projects as well, which can make it easier to target the right audience and to produce educational material to help users make a decision.

From the development side, as the backstop deposits become bigger and more widely adopted inside the MakerDAO protocol additional development could help it grow, for example:

  1. Collateral specific implementation that help optimizing the process.
  2. Risk/Reward adjustment for the idle deposits. E.g., put some of the backstop funds in a riskier yield bearing platform (e.g., yearn instead of compound or the DSR).
  3. Building the right incentive scheme for backstop contributors.

To summarize, the proposed system can work as is and already brings an improvement over the current liquidation system. However, after it proves useful with one collateral and certain level of deposits, continues work will be needed to further improve it.

2 Likes

Could the B.Protocol ETH AMM be configured to keep most of the ETH purchased as a way to address Proposal Idea - Purchase ETH for Strategic Reserves [Poll]? It seems likely that ETH purchased as part of the liquidation process would usually be acquired relatively cheap compared to the market price.

1 Like

Technically it could, but it would make sense to do it only for funds that are supplied by Maker and for a bounded duration of time.

I don’t think that for handling liquidations it is a viable long term strategy.

1 Like

Theoretically that is possible but by copying the code you won’t get access to the liquidity which Bprotocol has available. Maker could copy the code and source its own liquidity but this would also cost money.

At the end if the day its a question if Maker should become a big monolithic dApp or if they should just use the money lego which is available.

I believe that it is in Maker’s interest to implement Bprotocol and focus on its core business instead of trying to do everything and losing focus.

2 Likes