-
Notifications
You must be signed in to change notification settings - Fork 2
add liquidation flag #37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ab860b8
05f1d4a
284bbc6
b4e2497
6df3215
4ecffd8
86c6ae7
fc1e0a8
dae7a02
154dded
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -451,7 +451,7 @@ access(all) contract TidalProtocol { | |
| if pullFromTopUpSource && position.topUpSource != nil { | ||
| let topUpSource = position.topUpSource! | ||
| let sourceType = topUpSource.getSourceType() | ||
| let sourceAmount = topUpSource.minimumAvailable() | ||
| let sourceAmount = topUpSource.minimumAvailable(liquidate: false) | ||
| log(" [CONTRACT] Calling to fundsAvailableAboveTargetHealthAfterDepositing with sourceAmount \(sourceAmount) and targetHealth \(position.minHealth)") | ||
|
|
||
| return self.fundsAvailableAboveTargetHealthAfterDepositing( | ||
|
|
@@ -471,6 +471,95 @@ access(all) contract TidalProtocol { | |
| } | ||
| } | ||
|
|
||
| /// Simulates the withdrawable amount of `type` if the position were fully rebalanced (liquidation scenario). | ||
| /// Returns the amount in units of `type`. | ||
| access(all) fun simulateLiquidationAmount(pid: UInt64, type: Type): UFix64 { | ||
| let position = self._borrowPosition(pid: pid) | ||
|
|
||
| // If there's no top-up source configured, nothing can be pulled during liquidation. | ||
| if position.topUpSource == nil { | ||
| log("simulateLiquidationAmount: no topUpSource found for position \(pid) - returning 0.0") | ||
| return 0.0 | ||
| } | ||
|
|
||
| let topUpSource = position.topUpSource! | ||
| let sourceType = topUpSource.getSourceType() | ||
|
|
||
| // Prices (in default token units) | ||
| let maybeDepositTokenPrice = self.priceOracle.price(ofToken: type) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't this the price of type being withdrawn?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, it is, I think the confusion is between deposit as a noun and deposit as a verb, I'll rename it |
||
| let maybeSourceTokenPrice = self.priceOracle.price(ofToken: sourceType) | ||
| if maybeDepositTokenPrice == nil || maybeSourceTokenPrice == nil { | ||
| log("simulateLiquidationAmount: missing price(s); returning 0.0") | ||
| return 0.0 | ||
| } | ||
| let uintDepositTokenPrice = DeFiActionsMathUtils.toUInt128(maybeDepositTokenPrice!) | ||
| let uintSourceTokenPrice = DeFiActionsMathUtils.toUInt128(maybeSourceTokenPrice!) | ||
|
|
||
| // Amount available from the top-up source under liquidation semantics | ||
| let sourceAmountUFix = topUpSource.minimumAvailable(liquidate: true) | ||
| let uintSourceAmount = DeFiActionsMathUtils.toUInt128(sourceAmountUFix) | ||
|
|
||
| // ----- Deposit token leg (this position's balance in `type`) ----- | ||
| let maybeDepositBalance = position.balances[type] | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Couldn't a balance also exist for withdrawals on a type? I think the variable name is throwing me off since I'd think the balance returned is the amount available for withdrawal from the position, which in my mind conflicts with Also, I think we need to check the full position balance across all withdrawals, not just the source type and provided type. Once we're dealing with balances related to more than two type, I think the balances of the third type (and beyond) will be omitted from this calculation. For instance, let's say the topUpSource provides MOET and the requested Type is FLOW, how would this method consider a credit or debit in a third token type? |
||
| var depositCreditQuote: UInt128 = 0 // value (in default token) of credit in `type` | ||
| var depositDebtQuote: UInt128 = 0 // value (in default token) of debt in `type` | ||
|
|
||
| if maybeDepositBalance != nil { | ||
| let depositTokenState = self._borrowUpdatedTokenState(type: type) | ||
| let bal = maybeDepositBalance! | ||
| if bal.direction == BalanceDirection.Credit { | ||
| let trueCredit = TidalProtocol.scaledBalanceToTrueBalance( | ||
| bal.scaledBalance, | ||
| interestIndex: depositTokenState.creditInterestIndex | ||
| ) | ||
| depositCreditQuote = DeFiActionsMathUtils.mul(trueCredit, uintDepositTokenPrice) | ||
| } else { | ||
| let trueDebt = TidalProtocol.scaledBalanceToTrueBalance( | ||
| bal.scaledBalance, | ||
| interestIndex: depositTokenState.debitInterestIndex | ||
| ) | ||
| depositDebtQuote = DeFiActionsMathUtils.mul(trueDebt, uintDepositTokenPrice) | ||
| } | ||
| } | ||
|
|
||
| // ----- Source token debt leg (debt in the top-up token we must cover first) ----- | ||
| let maybeSourceBalance = position.balances[sourceType] | ||
| var sourceDebtQuote: UInt128 = 0 | ||
| if maybeSourceBalance?.direction == BalanceDirection.Debit { | ||
| let sourceTokenState = self._borrowUpdatedTokenState(type: sourceType) | ||
| let trueSourceDebt = TidalProtocol.scaledBalanceToTrueBalance( | ||
| maybeSourceBalance!.scaledBalance, | ||
| interestIndex: sourceTokenState.debitInterestIndex | ||
| ) | ||
| sourceDebtQuote = DeFiActionsMathUtils.mul(trueSourceDebt, uintSourceTokenPrice) | ||
| } | ||
|
|
||
| // ----- Source token available (credit we can pull during liquidation) ----- | ||
| let sourceAvailQuote = DeFiActionsMathUtils.mul(uintSourceAmount, uintSourceTokenPrice) | ||
|
|
||
| // Net value in default-token quote terms | ||
| // netQuote = depositCreditQuote + sourceAvailQuote - sourceDebtQuote - depositDebtQuote | ||
| var netQuote: UInt128 = 0 | ||
| let additions = depositCreditQuote + sourceAvailQuote | ||
| let subtractions = sourceDebtQuote + depositDebtQuote | ||
| if additions > subtractions { | ||
| netQuote = additions - subtractions | ||
| } else { | ||
| // Nothing left for withdrawal in `type` after covering debts | ||
| log("simulateLiquidationAmount: net quote is non-positive; returning 0.0") | ||
| return 0.0 | ||
| } | ||
|
|
||
| // Convert net quote value back into `type` units | ||
| if uintDepositTokenPrice == 0 { | ||
| log("simulateLiquidationAmount: deposit token price is zero; returning 0.0") | ||
| return 0.0 | ||
| } | ||
| let uintLiquidationAmountInType = DeFiActionsMathUtils.div(netQuote, uintDepositTokenPrice) | ||
|
|
||
| return DeFiActionsMathUtils.toUFix64Round(uintLiquidationAmountInType) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would think we want to round down here since this amount denotes a withdrawal from a position. Is that right? |
||
| } | ||
|
|
||
| /// Returns the health of the given position, which is the ratio of the position's effective collateral to its | ||
| /// debt as denominated in the Pool's default token. "Effective collateral" means the value of each credit balance | ||
| /// times the liquidation threshold for that token. i.e. the maximum borrowable amount | ||
|
|
@@ -1825,11 +1914,14 @@ access(all) contract TidalProtocol { | |
| return self.type | ||
| } | ||
| /// Returns the minimum availble this Source can provide on withdrawal | ||
| access(all) fun minimumAvailable(): UFix64 { | ||
| access(all) fun minimumAvailable(liquidate: Bool): UFix64 { | ||
| if !self.pool.check() { | ||
| return 0.0 | ||
| } | ||
| let pool = self.pool.borrow()! | ||
| if liquidate { | ||
| return pool.simulateLiquidationAmount(pid: self.positionID, type: self.type) | ||
| } | ||
| return pool.availableBalance(pid: self.positionID, type: self.type, pullFromTopUpSource: self.pullFromTopUpSource) | ||
| } | ||
| /// Withdraws up to the max amount as the sourceType Vault | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm realizing we may be overloading the term "liquidation" with this. When we say "liquidation" here, does this have anything to do with the liquidation path in the sense one would think of with respect to a lending protocol? In other words, are we using liquidation here to mean fully exiting the position or reclaiming collateral in the case of bad debt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in this case it means fully closing/exiting a position at that moment