Minimax explained: stop-loss for DeFi. Part 1

Staking

Suppose you find an attractive liquidity or lending pool with 50% or higher return rate.

Such investment opportunities usually involve volatile tokens, so you can’t just invest and forget. You’ll have to monitor your asset price in order to withdraw your deposit before a price drop. How might you protect your assets from depreciation? No need to go very far: there’s already a time-tested mechanism: “stop-loss”. It’s implemented on the majority of centralized crypto (and not only crypto) exchanges. But do we find anything similar in the decentralized world? Some distributed, open-source, on-chain and audited platform?

Here comes Minimax.Finance — a decentralized app that aggregates vaults from other platforms. It gathers and displays information about vaults, including APR and APY, and provides a unified public interface for interacting with them (web user interface, as well as smart contract ABI). On top of that the app offers configurable “stop-loss” and “take-profit” parameters.

1_zYTsmoPDx0OD2k7ZyZKOXw.png

Investors interact only with the main contract. They simply choose the vault, specify the parameters, some magic happens et voila — the deposit goes to the selected vault on a third-party protocol.

Decentralized stop loss

Implementing a stop loss may seem to be a trivial task for anyone who has programming experience: when the price goes below a certain limit — withdraw all assets from a pool and convert them to “stablecoins”. But the words “when” and “price” have a bit different meaning on a decentralized blockchain. Ethereum-compatible smart contracts don’t have a way to trigger code execution when some conditions are met. Of course we could run our backend service that would monitor prices and liquidate positions, but it doesn’t look like something users would trust a lot. So, if we have no other choice but to use an off-chain service, how can we make this process more transparent and reliable? Let it be an external trusted service — gelato.network. Gelato monitors on-chain contracts and executes functions when conditions are met. This is how it looks in the code: a contract has two functions “resolve” and “exec”. Gelato frequently observes what the “resolve” returns, and as soon as the returned value becomes “true”, Gelato calls “exec”. Here is a pseudocode snippet: (the actual code can be found in the Minimax main contract).

function resolve (uint positionIndex) view {
return stakedTokenPrice(positionIndex) < stopLoss(positionIndex)
}
function exec (uint positionIndex) {
payGelatoFee()
withdrawFromPool(positionIndex)
exchangeToStable(positionIndex)
}

The second challenge we faced was finding the price on-chain, which is not a trivial task. First, let’s give a short overview of how exchanges on blockchain work. Imagine a contract that holds 1e7 (Scientific notation) of TokenA and 1e6 of TokenB. This contract is programmed to always preserve the following invariant: the multiplication of amounts before and after exchange is equal. So in our case, that multiplication equals 1e7 * 1e6 = 1e13. What if I want to exchange 1e2 of TokenA to TokenB, how much of TokenB will I receive? Let’s calculate. After adding 1e2 of TokenA to the balance it becomes equal to 10000100 of TokenA. What will the balance of TokenB be after that? TokenB = 1e13 / (1e7+1e2) ≈ 999990. So, the contract should transfer back 1e6–999990 = 10 of TokenB to preserve the invariant. This contract is easy to manipulate by sending a big amount of tokens to it and altering the price as a result. (more detailed articles on the topic of token price manipulation: https://arxiv.org/pdf/2003.03810.pdf, https://arxiv.org/pdf/2103.12732.pdf). More info on how DEXs work in this wiki article.

So instead of relying on DEXs, which can be manipulated, Minimax uses Chainlink price oracles — an external price provider. From a programmatic point of view, Chainlink price oracle is a contract, the data in which is updated externally by Chainlink, and can be consumed by any contract. Let’s check if the price provided by the price oracle matches the exchange price.

But no matter which price provider we use, the token swap is still performed via DEXs :). So what Minimax does is it verifies that the token price difference between the price oracle and a DEX is negligible. In order to express that in the code we introduce the parameter called slippage, with slippage = abs (dex price — pool price) / pool price. And exchange may happen only if the slippage is less than the user-defined value. What value of the slippage to select? Below are the charts and tables of the slippage that might help to select a default value. All charts and tables are from Binance Smart Chain from the range from block 12800000 to 15567245 (Nov-20–2021 — Feb-25–2022).

0_tNm9WMLipipvza7U.png Slippage histogram for CAKE token
0_f_7_NtQzaRQmRiSG.png
Slippage histogram for DOT token

The tables below represent how many blocks it usually takes for the slippage to get within the defined range for CAKE and DOT token. For example, if a 1% slippage is selected, then there’s a 98% chance that the liquidation will be executed with the first available block for both tokens, and 99% chance that no more than 5 blocks have to be mined in order for the slippage to become less than 1% for CAKE token and zero blocks for DOT token.

1_sERaDPDKKYWFnpaXrmg6ZA.png
Slippage distribution for CAKE token
1_OI9biXfJHb3KWqyMRHWZkQ.png
Slippage distribution for DOT token

Based on this we selected 2% slippage as a default value.

Thank you very much for reading this article. If you have any questions, please feel free to ask them in the comments or in our Telegram chat.

Useful links:
Minimax.Finance
Linktree
Twitter
Telegram
Discord
Medium
Subreddit

Coin Marketplace

STEEM 0.17
TRX 0.15
JST 0.029
BTC 61869.35
ETH 2414.51
USDT 1.00
SBD 2.63