Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
233 changes: 167 additions & 66 deletions docs/developer-guide/contracts/AmmalgamPair.sol/contract.AmmalgamPair.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# AmmalgamPair
[Git Source](https://github.com/Ammalgam-Protocol/core-v1/blob/2b185eab2df708b55f7ffa534655c69f626e73b3/contracts/AmmalgamPair.sol)
[Git Source](https://github.com/Ammalgam-Protocol/core-v1/blob/ec51218155bd2f8c1e5dc761ed4728baae81a01b/contracts/AmmalgamPair.sol)

**Inherits:**
[IAmmalgamPair](/docs/developer-guide/contracts/interfaces/IAmmalgamPair.sol/interface.IAmmalgamPair.md), [TokenController](/docs/developer-guide/contracts/tokens/TokenController.sol/contract.TokenController.md)
Expand Down Expand Up @@ -34,6 +34,13 @@ uint256 private transient locked;
```


### activeBorrower

```solidity
address private transient activeBorrower;
```


## Functions
### lock

Expand All @@ -56,6 +63,13 @@ function _lock() private;
function _unlock() private;
```

### _revertNestedBorrow


```solidity
function _revertNestedBorrow() private view;
```

### mint


Expand Down Expand Up @@ -110,11 +124,9 @@ function calculateAmountIn(
helper method to calculate balance after fees

*Note that amountIn + reserve does not always equal balance if amountOut > 0.
When assets are depleted, we should multiply (balance - missing) * BUFFER_NUMERATOR /
INVERSE_BUFFER, but instead of divide here, we multiply the other side of the K
comparison, see `calculateReserveAdjustmentsForMissingAssets` where we multiply by
INVERSE_BUFFER. When not depleted, we multiply by INVERSE_BUFFER instead of dividing on
the other side.*
In the depleted case the balance is scaled by `BUFFER_NUMERATOR`; the matching
scaling is applied on the reserve side in `calculateReserveAdjustmentsForMissingAssets`
so the K comparison stays division-free on both branches.*


```solidity
Expand All @@ -137,42 +149,6 @@ function calculateBalanceAfterFees(
|`missing`|`uint256`|the missing assets, zero if deposits > borrows of X or Y|


### calculateReserveAdjustmentsForMissingAssets

helper method to calculate balance adjustment for missing assets

*For swap, when assets are depleted, we should multiply (reserve - missing) by
BUFFER_NUMERATOR / INVERSE_BUFFER, but instead of divide here, we multiply the other
side of the K comparison, see `calculateBalanceAfterFees` where we multiply by
INVERSE_BUFFER.
For updateObservation, different scaled `buffer` and `bufferNumerator` values
are supplied so the adjusted reserve reflects observation-specific logic.*


```solidity
function calculateReserveAdjustmentsForMissingAssets(
uint256 reserve,
uint256 missing,
uint256 buffer,
uint256 bufferNumerator
) private pure returns (uint256 reserveAdjustment);
```
**Parameters**

|Name|Type|Description|
|----|----|-----------|
|`reserve`|`uint256`|the starting reserve|
|`missing`|`uint256`|the missing assets, zero if deposits > borrows of X or Y|
|`buffer`|`uint256`| Scaling factor applied to the reserve for the depletion comparison.|
|`bufferNumerator`|`uint256`| Scaling factor applied to the missing amount for the comparison and for computing the depleted-case adjusted reserve.|

**Returns**

|Name|Type|Description|
|----|----|-----------|
|`reserveAdjustment`|`uint256`|The adjusted reserve value used for swap or updateObservation depends on the buffer, bufferNumerator to be passed in.|


### deposit


Expand Down Expand Up @@ -338,7 +314,85 @@ function liquidate(

LTV based liquidation. The LTV dictates the max premium that can be had by the
liquidator. We determine the amount of borrowed liquidity to be repaid by reducing the
actual amount paid by the amount passed in for the borrow x and borrow y amount.
actual amount transferred prior to calling `liquidate()` by the amount passed in for the
repayXAssets and repayYAssets parameters.
## Calculating a Hard Liquidation
Hard liquidations can be partial subset of the borrower's entire position based on
tranche saturation composition. Use `LiquidationUtils.calculatePartialLiquidationAmount()`
to compute the partial position, max premium, and the amounts needed for the call.
Three prices are in play and each is used at a different stage:
| Price | Source | Where used |
|---------------------|-------------------------|------------------------------------|
| Reserve price | reserveX / reserveY | `_repayLiquidity`: splits tokens |
| | | into the X/Y ratio the pool needs |
| | | to burn BORROW_L. |
| sqrtPriceMin (TWAP) | geometric TWAP low tick | Verification converts: |
| | | BORROW_X → L (worst-case borrow) |
| | | DEPOSIT_Y → L (worst-case dep.) |
| sqrtPriceMax (TWAP) | geometric TWAP high tick| Verification converts: |
| | | BORROW_Y → L (worst-case borrow) |
| | | DEPOSIT_X → L (worst-case dep.) |
Verification (`verifyHardLiquidation`) swaps min and max when calling
`getCheckLtvParams(proposed, sqrtPriceMax, sqrtPriceMin)`. This values deposits at
their highest and borrows at their lowest, favoring the borrower by making the
position appear as healthy as possible. This limits the ability of liquidators to
manipulate premiums.
The reserve price and TWAP prices may diverge. Seized deposit amounts must be
computed using TWAP prices (the verification domain), not reserve prices.
### Net Debt Calculation and the BORROW_L Overlap
`getBorrowedInL` initializes BOTH sides from BORROW_L:
```text
netBorrowedXinL = BORROW_L + convertXToL(BORROW_X, sqrtPriceMin)
netBorrowedYinL = BORROW_L + convertYToL(BORROW_Y, sqrtPriceMax)
```
`getDepositsInL` initializes BOTH sides from DEPOSIT_L:
```text
netDepositedXinL = DEPOSIT_L + convertXToL(DEPOSIT_X, sqrtPriceMax)
netDepositedYinL = DEPOSIT_L + convertYToL(DEPOSIT_Y, sqrtPriceMin)
```
`calcDebtAndCollateral` then nets these to determine `netDebtX`:
```text
netDebtX = true when netDepositedX <= netBorrowedX AND netDepositedY > netBorrowedY
netDebtX = false when netDepositedY <= netBorrowedY AND netDepositedX > netBorrowedX
```
`netDebtX` selects the saturation account. A wrong value causes
`calculatePartialLiquidation` to return the wrong tranche set, reverting the
liquidation.
### Computing Seized amounts Borrow X, Seize Y
For a position with BORROW_X and DEPOSIT_Y, the seized Y only needs
to cover the liquidation premium on the repaid debt:
```text
borrowXInL = ceil(BORROW_X * Q72 / sqrtPriceMax)
premiumInL = borrowXInL * maxPremiumBips / BIPS
seizedY = premiumInL * Q72 / sqrtPriceMax
```
### Overlap Case: Borrow X + Borrow L, Seize Y
When BORROW_L is present and BORROW_Y is zero, `getBorrowedInL` computes:
```text
netBorrowedXinL = BORROW_L + convertXToL(BORROW_X, sqrtPriceMin)
netBorrowedYinL = BORROW_L
```
The seized Y (in L-terms at TWAP price) must exceed BORROW_L so that
`netDepositedYinL > netBorrowedYinL`, which is required for `netDebtX = true`.
The seized amount includes the BORROW_L overlap plus the liquidation premium:
```text
borrowXInL = ceil(BORROW_X * Q72 / sqrtPriceMax)
netRepaidInL = BORROW_L + borrowXInL
premiumInL = netRepaidInL * maxPremiumBips / BIPS
seizedYInL = BORROW_L + premiumInL
seizedY = seizedYInL * Q72 / sqrtPriceMax
```
### Token Transfer Amounts
The liquidator must transfer enough tokens to cover both direct borrows and BORROW_L:
```text
repayLX = ceil(BORROW_L * reserveX / activeLiquidity)
repayLY = ceil(BORROW_L * reserveY / activeLiquidity)
totalXTransfer = BORROW_X + repayLX
totalYTransfer = repayLY
```
These use reserve prices because `_repayLiquidity` splits tokens by the reserve ratio.
The seized Y amount uses TWAP prices because `verifyHardLiquidation` operates in that
domain.


```solidity
Expand Down Expand Up @@ -369,14 +423,7 @@ Liquidation based on change of saturation because of time.


```solidity
function resetSaturation(
Validation.InputParams memory inputParams,
address borrower,
address to,
uint256 depositLToBeTransferredInLAssets,
uint256 depositXToBeTransferredInXAssets,
uint256 depositYToBeTransferredInYAssets
) private;
function resetSaturation(Validation.InputParams memory inputParams, address borrower, address to) private;
```
**Parameters**

Expand All @@ -385,9 +432,6 @@ function resetSaturation(
|`inputParams`|`Validation.InputParams`||
|`borrower`|`address`|The account being liquidated.|
|`to`|`address`|The account to send the liquidated deposit to|
|`depositLToBeTransferredInLAssets`|`uint256`|The amount of L to be transferred to the liquidator.|
|`depositXToBeTransferredInXAssets`|`uint256`|The amount of X to be transferred to the liquidator.|
|`depositYToBeTransferredInYAssets`|`uint256`|The amount of Y to be transferred to the liquidator.|


### liquidateLeverage
Expand All @@ -400,24 +444,66 @@ function liquidateLeverage(
Validation.InputParams memory inputParams,
address borrower,
address to,
uint256 repaidXAssets,
uint256 repaidYAssets,
bool depositL,
bool repayL
uint256 actualRepaidXAssets,
uint256 actualRepaidYAssets
) private;
```
**Parameters**

|Name|Type|Description|
|----|----|-----------|
|`inputParams`|`Validation.InputParams`||
|`inputParams`|`Validation.InputParams`|The input parameters for the liquidation, including reserves and price.|
|`borrower`|`address`|The account being liquidated.|
|`to`|`address`|The account to send the liquidated deposit to|
|`repaidXAssets`|`uint256`||
|`repaidYAssets`|`uint256`||
|`depositL`|`bool`|Flag indicating whether the deposit transferred to the liquidator is L xor X+Y.|
|`repayL`|`bool`|Flag indicating whether the repay by the liquidator is L xor X+Y.|
|`to`|`address`|The account to send the liquidated deposit to.|
|`actualRepaidXAssets`|`uint256`|The actual amount of X assets repaid by the liquidator.|
|`actualRepaidYAssets`|`uint256`|The actual amount of Y assets repaid by the liquidator.|


### _liquidationRepayHelper

Repays the borrow legs of a liquidation and verifies the liquidator repaid enough.

*Shared by hard and leverage liquidations. The required X/Y repayments must be fully
covered by the assets the liquidator sent in; any remainder repays borrowed liquidity.
`liquidationParams[BORROW_L]` carries the minimum liquidity that must be repaid and is
overwritten with the liquidity actually repaid so `verifyHardLiquidation` can read it back.*


```solidity
function _liquidationRepayHelper(
address borrower,
uint256[6] memory liquidationParams,
uint256 actualRepaidXAssets,
uint256 actualRepaidYAssets,
uint256 _reserveXAssets,
uint256 _reserveYAssets
) private;
```
**Parameters**

|Name|Type|Description|
|----|----|-----------|
|`borrower`|`address`|The account being liquidated.|
|`liquidationParams`|`uint256[6]`|Borrow legs to repay, indexed by BORROW_X / BORROW_Y / BORROW_L.|
|`actualRepaidXAssets`|`uint256`|The X assets the liquidator transferred in for the repayment.|
|`actualRepaidYAssets`|`uint256`|The Y assets the liquidator transferred in for the repayment.|
|`_reserveXAssets`|`uint256`|Current X reserves, used to split repaid liquidity by the reserve ratio.|
|`_reserveYAssets`|`uint256`|Current Y reserves, used to split repaid liquidity by the reserve ratio.|


### _revertNotEnoughRepaidForLiquidation


```solidity
function _revertNotEnoughRepaidForLiquidation() private pure;
```

### _burnBadDebt


```solidity
function _burnBadDebt(address borrower, uint256[6] memory userAssets) private;
```

### finalizeLiquidation

Expand Down Expand Up @@ -510,12 +596,27 @@ function validateOnUpdate(address validate, address update, bool alwaysUpdate) p
function validateSolvency(address validate, bool alwaysUpdate) private;
```

### getInputParamsAndUpdateSaturation
### updateSaturationIfNeeded

Update saturation state for an account if it already exists in saturation.

*Note that during a repay of debt, we may not have an entry in saturation if
1. The position is a straddle with a payout that never reaches zero
2. Repay is occurring during a callback of a flash loan, saturation will not be updated
until the end of the borrow call after the callback concludes.*


```solidity
function getInputParamsAndUpdateSaturation(address toUpdate, bool alwaysUpdate) private;
function updateSaturationIfNeeded(
address toUpdate
) private;
```
**Parameters**

|Name|Type|Description|
|----|----|-----------|
|`toUpdate`|`address`|The account to update saturation for.|


### getInputParams

Expand Down
Loading