Skip to content

Commit

Permalink
Merge pull request #269 from reflexer-labs/savior-balance-fix
Browse files Browse the repository at this point in the history
Savior balance fix
  • Loading branch information
mstfash authored Oct 25, 2021
2 parents d2191c9 + e849d04 commit 5c27154
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 76 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
"@walletconnect/web3-provider": "1.1.1-alpha.0",
"@web3-react/core": "^6.0.9",
"@web3-react/injected-connector": "^6.0.7",
"@web3-react/walletconnect-connector": "^6.1.1",
"@web3-react/walletlink-connector": "^6.0.9",
"@web3-react/walletconnect-connector": "6.2.4",
"@web3-react/walletlink-connector": "6.2.3",
"async-retry": "^1.3.1",
"axios": "^0.20.0",
"classnames": "^2.2.6",
Expand Down
1 change: 1 addition & 0 deletions src/components/SafeOperations/SafeBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,7 @@ const Value = styled.div`

const Body = styled.div`
padding: 20px;
position: relative;
`

const Footer = styled.div`
Expand Down
30 changes: 23 additions & 7 deletions src/containers/OnBoarding/SafeDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useEffect, useState } from 'react'
import React, { useCallback, useEffect, useState } from 'react'
import { Link2 } from 'react-feather'
import { useTranslation } from 'react-i18next'
import { useHistory } from 'react-router'
import { useHistory } from 'react-router-dom'
import styled from 'styled-components'
import AlertLabel from '../../components/AlertLabel'
import Button from '../../components/Button'
Expand All @@ -25,11 +25,13 @@ const SafeDetails = ({ ...props }) => {
const { account, library } = useActiveWeb3React()
const [loading, setIsLoading] = useState(false)
const geb = useGeb()
const {
safeModel: safeActions,
popupsModel: popupsActions,
} = useStoreActions((state) => state)
const { safeModel: safeState } = useStoreState((state) => state)
const { safeModel: safeActions, popupsModel: popupsActions } =
useStoreActions((state) => state)
const { safeModel: safeState, connectWalletModel: connectWalletState } =
useStoreState((state) => state)

const { fiatPrice: ethPrice } = connectWalletState

const safeId = props.match.params.id as string

const hasSaviour = useHasSaviour(
Expand Down Expand Up @@ -106,6 +108,20 @@ const SafeDetails = ({ ...props }) => {
safeId,
])

const fetchSaviourDataCallback = useCallback(() => {
if (!account || !geb || !safeId) return
safeActions.fetchSaviourData({
account,
geb,
safeId,
ethPrice,
})
}, [account, ethPrice, geb, safeActions, safeId])

useEffect(() => {
fetchSaviourDataCallback()
}, [fetchSaviourDataCallback])

const handleSaviourBtnClick = async (data: {
status: boolean
saviourAddress: string
Expand Down
128 changes: 61 additions & 67 deletions src/hooks/useSaviour.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ import {
SaviourWithdrawPayload,
} from '../utils/interfaces'
import { BigNumber } from '@ethersproject/bignumber'
import { formatNumber } from '../utils/helper'
import { formatNumber, toFixedString } from '../utils/helper'

export const LIQUIDATION_POINT = 125 // percent
export const LIQUIDATION_CRATIO = 135 // percent

export type SaviourData = {
safeId: string
Expand Down Expand Up @@ -202,7 +203,6 @@ export async function fetchSaviourData({
const uniPoolPrice = numeral(numerator)
.divide(formattedCoinTotalSupply)
.value()

return {
safeId,
hasSaviour: saviourAddress !== EMPTY_ADDRESS,
Expand Down Expand Up @@ -237,107 +237,101 @@ export function useSaviourData(): SaviourData | undefined {
return saviourData
}

export function useTargetedCRatio(): number {
const { safeModel: safeState } = useStoreState((state) => state)
const { targetedCRatio } = safeState
return targetedCRatio
}

// minSaviourBalance

export function useMinSaviourBalance() {
const HUNDRED = 100

const saviourData = useSaviourData()

const getMinSaviourBalance = useCallback(
(targetCRatio: number) => {
(
targetCRatio?: number,
totalDebt?: string,
totalCollateral?: string
) => {
const WAD_COMPLEMENT = BigNumber.from(10 ** 9)
if (!saviourData || !targetCRatio) return '0'
const { RAY } = gebUtils
const {
redemptionPrice,
generatedDebt,
generatedDebt: safeDebt,
accumulatedRate,
lockedCollateral,
reserveETH: ethReserve,
reserveRAI: raiReserve,
coinTotalSupply: lpTotalSupply,
lockedCollateral: safeCollateral,
keeperPayOut,
} = saviourData

const generatedDebt = totalDebt
? BigNumber.from(toFixedString(totalDebt, 'WAD'))
.mul(RAY)
.div(accumulatedRate)
: safeDebt

const lockedCollateral = totalCollateral
? BigNumber.from(toFixedString(totalCollateral, 'WAD'))
: safeCollateral

// Liquidation price formula
//
// debt * accumulatedRate * targetCRatio * RP
// debt * accumulatedRate * RP
// liquidationPrice = -----------------------------------------------
// collateral

const liquidationPrice = !generatedDebt.isZero()
? redemptionPrice
.mul(generatedDebt.mul(WAD_COMPLEMENT))
.mul(accumulatedRate)
.div(lockedCollateral.mul(WAD_COMPLEMENT))
.div(RAY)
.mul(LIQUIDATION_POINT)
.div(HUNDRED)
.div(lockedCollateral.mul(WAD_COMPLEMENT))
.div(RAY)
: BigNumber.from('0')

// Formula for min savior balance
//
// targetCRatio * RP * accumulatedRate * debt - collateralPrice * collateral
// Min savior balance = ----------------------------------------------------------------------------------------------------------------------
// collateralPrice * (reserveETH / totalLPsupply) + RP * accumulatedRate * (reserveRAI / totalLPsupply) * targetCRatio
// The calculation below refers to the formula described at:
// https://docs.reflexer.finance/liquidation-protection/uni-v2-rai-eth-savior-math

// (All calculation are made in RAY)
const numerator = redemptionPrice
const jVar = redemptionPrice
.mul(accumulatedRate)
.div(RAY)
.mul(generatedDebt.mul(WAD_COMPLEMENT))
.div(RAY)
.mul(targetCRatio)
.div(100)
.sub(
liquidationPrice
.mul(lockedCollateral)
.mul(WAD_COMPLEMENT)
.div(RAY)
)

const denominator = liquidationPrice
.mul(ethReserve.mul(WAD_COMPLEMENT))
.div(lpTotalSupply.mul(WAD_COMPLEMENT))
.add(
redemptionPrice
.mul(accumulatedRate)
.div(RAY)
.mul(raiReserve.mul(WAD_COMPLEMENT))
.div(lpTotalSupply.mul(WAD_COMPLEMENT))
.mul(targetCRatio)
.div(100)
)

let balanceBN = !generatedDebt.isZero()
? numerator.mul(RAY).div(denominator)
: BigNumber.from('0')

// Price USD RAY price of a LP share
// lpUsdPrice = (reserveETH * priceEth + reserveRAI * priceRAI) / lpTotalSupply
const lpTokenUsdPrice = ethReserve
.mul(WAD_COMPLEMENT)
.mul(liquidationPrice)
.div(RAY)
.add(
raiReserve.mul(WAD_COMPLEMENT).mul(redemptionPrice).div(RAY)
)
.div(HUNDRED)
.mul(RAY)
.div(lpTotalSupply.mul(WAD_COMPLEMENT))
.div(liquidationPrice)

// Calculate keeper fee and add it to the min balance
const keeperPayoutInLP = keeperPayOut
.mul(WAD_COMPLEMENT)
.mul(RAY)
.div(lpTokenUsdPrice)
// TODO: Rai market price as RAY
// const currentRaiMarketPrice = BigNumber.from(
// '3050000000000000000000000000'
// )

balanceBN = !generatedDebt.isZero()
? balanceBN.add(keeperPayoutInLP)
: BigNumber.from('0')
const pVar = redemptionPrice.mul(RAY).div(liquidationPrice)

const minSaviorBalance = parseInt(balanceBN.toString()) / 1e27
return formatNumber(minSaviorBalance.toString(), 4, true)
// Leave out sqrt(p) from the minimum bal equation because BignNumber doesn't do square root
const minSaviorBalanceRayWithoutSqrtP = lockedCollateral
.mul(WAD_COMPLEMENT)
.sub(generatedDebt.mul(WAD_COMPLEMENT).mul(jVar).div(RAY))
.div(jVar.add(pVar))
// TODO: Find a better way doing square root if there is
const minSaviorBalanceNumber =
Math.sqrt(Number(pVar.toString()) / 1e27) *
Number(minSaviorBalanceRayWithoutSqrtP.toString())

const keeperPayoutInLP =
Number(keeperPayOut.mul(WAD_COMPLEMENT).toString()) /
(Math.sqrt(
Number(liquidationPrice.mul(redemptionPrice).toString())
) *
2)

// Add the keeper balance
const minSaviorBalanceFinal =
Math.abs(minSaviorBalanceNumber) +
Number(keeperPayoutInLP.toString())

return formatNumber(minSaviorBalanceFinal.toString(), 4, true)
},
[saviourData]
)
Expand Down

0 comments on commit 5c27154

Please sign in to comment.