Skip to content

Commit

Permalink
submit offer before concentration check
Browse files Browse the repository at this point in the history
  • Loading branch information
0xddong committed Aug 15, 2024
1 parent 5d5c621 commit a929647
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 91 deletions.
13 changes: 10 additions & 3 deletions src/RepoTokenList.sol
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,14 @@ library RepoTokenList {

address current = listData.head;
while (current != NULL_NODE) {
// Filter by a specific repoToken, address(0) bypasses this filter
if (repoTokenToMatch != address(0) && current != repoTokenToMatch) {
// Not a match, do not add to totalPresentValue
// Move to the next token in the list
current = _getNext(listData, current);
continue;
}

uint256 currentMaturity = getRepoTokenMaturity(current);
uint256 repoTokenBalance = ITermRepoToken(current).balanceOf(address(this));
uint256 repoTokenPrecision = 10**ERC20(current).decimals();
Expand All @@ -217,10 +225,9 @@ library RepoTokenList {
totalPresentValue += repoTokenBalanceInBaseAssetPrecision;
}

// If filtering by a specific repo token, stop early if matched
// Filter by a specific repo token, address(0) bypasses this condition
if (repoTokenToMatch != address(0) && current == repoTokenToMatch) {
// matching a specific repoToken and terminate early because the list is sorted
// with no duplicates
// Found a match, terminate early
break;
}

Expand Down
131 changes: 44 additions & 87 deletions src/Strategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -302,22 +302,30 @@ contract Strategy is BaseStrategy, Pausable, ReentrancyGuard {
revert RepoTokenList.InvalidRepoToken(address(repoToken));
}

repoTokenListData.validateRepoToken(
uint256 redemptionTimestamp = repoTokenListData.validateRepoToken(
ITermRepoToken(repoToken),
address(asset)
);

uint256 discountRate = discountRateAdapter.getDiscountRate(repoToken);
uint256 repoTokenPrecision = 10 ** ERC20(repoToken).decimals();
uint256 repoTokenAmountInBaseAssetPrecision = (ITermRepoToken(
repoToken
).redemptionValue() *
amount *
PURCHASE_TOKEN_PRECISION) /
(repoTokenPrecision * RepoTokenUtils.RATE_PRECISION);
uint256 proceeds = RepoTokenUtils.calculatePresentValue(
repoTokenAmountInBaseAssetPrecision,
PURCHASE_TOKEN_PRECISION,
redemptionTimestamp,
discountRate + discountRateMarkup
);

_validateRepoTokenConcentration(
repoToken,
repoTokenAmountInBaseAssetPrecision,
0
proceeds
);
}

Expand Down Expand Up @@ -481,45 +489,6 @@ contract Strategy is BaseStrategy, Pausable, ReentrancyGuard {
revert RepoTokenConcentrationTooHigh(repoToken);
}
}

/**
* @notice Validates the resulting weighted time to maturity when submitting a new offer
* @param repoToken The address of the repoToken associated with the Term auction offer
* @param newOfferAmount The amount associated with the Term auction offer
* @param newLiquidBalance The new liquid balance of the strategy after accounting for the new offer
*
* @dev This function calculates the resulting weighted time to maturity assuming that a submitted offer is accepted
* and checks if it exceeds the predefined threshold (`timeToMaturityThreshold`). If the threshold is exceeded,
* the function reverts the transaction with an error.
*/
function _validateWeightedMaturity(
address repoToken,
uint256 newOfferAmount,
uint256 newLiquidBalance
) private {
// Calculate the precision of the repoToken
uint256 repoTokenPrecision = 10 ** ERC20(repoToken).decimals();

// Convert the new offer amount to repoToken precision
uint256 offerAmountInRepoPrecision = RepoTokenUtils
.purchaseToRepoPrecision(
repoTokenPrecision,
PURCHASE_TOKEN_PRECISION,
newOfferAmount
);

// Calculate the resulting weighted time to maturity
uint256 resultingWeightedTimeToMaturity = _calculateWeightedMaturity(
repoToken,
offerAmountInRepoPrecision,
newLiquidBalance
);

// Check if the resulting weighted time to maturity exceeds the threshold
if (resultingWeightedTimeToMaturity > timeToMaturityThreshold) {
revert TimeToMaturityAboveThreshold();
}
}

/**
* @notice Calculates the weighted time to maturity for the strategy's holdings, including the impact of a specified repoToken and amount
Expand Down Expand Up @@ -686,8 +655,6 @@ contract Strategy is BaseStrategy, Pausable, ReentrancyGuard {
address(asset)
);

_validateRepoTokenConcentration(repoToken, purchaseTokenAmount, 0);

// Prepare and submit the offer
ITermAuctionOfferLocker offerLocker = ITermAuctionOfferLocker(
termAuction.termAuctionOfferLocker()
Expand Down Expand Up @@ -739,54 +706,12 @@ contract Strategy is BaseStrategy, Pausable, ReentrancyGuard {
_sweepAsset();
_redeemRepoTokens(0);

// Retrieve the total liquid balance
uint256 liquidBalance = _totalLiquidBalance(address(this));

uint256 newOfferAmount = purchaseTokenAmount;
bytes32 offerId = _generateOfferId(idHash, address(offerLocker));
uint256 newOfferAmount = purchaseTokenAmount;
uint256 currentOfferAmount = termAuctionListData
.offers[offerId]
.offerAmount;

// Handle adjustments if editing an existing offer
if (newOfferAmount > currentOfferAmount) {
// increasing offer amount
uint256 offerDebit;
unchecked {
// checked above
offerDebit = newOfferAmount - currentOfferAmount;
}
if (liquidBalance < offerDebit) {
revert InsufficientLiquidBalance(liquidBalance, offerDebit);
}
uint256 newLiquidBalance = liquidBalance - offerDebit;
if (newLiquidBalance < liquidityReserveRatio) {
revert BalanceBelowliquidityReserveRatio();
}
_validateWeightedMaturity(
repoToken,
newOfferAmount,
newLiquidBalance
);
} else if (currentOfferAmount > newOfferAmount) {
// decreasing offer amount
uint256 offerCredit;
unchecked {
offerCredit = currentOfferAmount - newOfferAmount;
}
uint256 newLiquidBalance = liquidBalance + offerCredit;
if (newLiquidBalance < liquidityReserveRatio) {
revert BalanceBelowliquidityReserveRatio();
}
_validateWeightedMaturity(
repoToken,
newOfferAmount,
newLiquidBalance
);
} else {
// no change in offer amount, do nothing
}

// Submit the offer and lock it in the auction
ITermAuctionOfferLocker.TermAuctionOfferSubmission memory offer;
offer.id = currentOfferAmount > 0 ? offerId : idHash;
Expand All @@ -795,6 +720,7 @@ contract Strategy is BaseStrategy, Pausable, ReentrancyGuard {
offer.amount = purchaseTokenAmount;
offer.purchaseToken = address(asset);

// InsufficientLiquidBalance checked inside _submitOffer
offerIds = _submitOffer(
termAuction,
offerLocker,
Expand All @@ -803,6 +729,30 @@ contract Strategy is BaseStrategy, Pausable, ReentrancyGuard {
newOfferAmount,
currentOfferAmount
);

// Retrieve the total liquid balance
uint256 liquidBalance = _totalLiquidBalance(address(this));

// Check that new offer does not violate reserve ratio constraint
if (liquidBalance < liquidityReserveRatio) {
revert BalanceBelowliquidityReserveRatio();
}

// Calculate the resulting weighted time to maturity
// Passing in 0 adjustment because offer and balance already updated
uint256 resultingWeightedTimeToMaturity = _calculateWeightedMaturity(
address(0),
0,
liquidBalance
);

// Check if the resulting weighted time to maturity exceeds the threshold
if (resultingWeightedTimeToMaturity > timeToMaturityThreshold) {
revert TimeToMaturityAboveThreshold();
}

// Passing in 0 amount and 0 liquid balance adjustment because offer and balance already updated
_validateRepoTokenConcentration(repoToken, 0, 0);
}

/**
Expand Down Expand Up @@ -842,6 +792,12 @@ contract Strategy is BaseStrategy, Pausable, ReentrancyGuard {
// checked above
offerDebit = newOfferAmount - currentOfferAmount;
}

uint256 liquidBalance = _totalLiquidBalance(address(this));
if (liquidBalance < offerDebit) {
revert InsufficientLiquidBalance(liquidBalance, offerDebit);
}

_withdrawAsset(offerDebit);
IERC20(asset).safeApprove(
address(repoServicer.termRepoLocker()),
Expand Down Expand Up @@ -1002,7 +958,8 @@ contract Strategy is BaseStrategy, Pausable, ReentrancyGuard {
}

// Ensure the remaining liquid balance is above the liquidity threshold
if ((liquidBalance - proceeds) < liquidityReserveRatio) {
uint256 newLiquidBalance = liquidBalance - proceeds;
if (newLiquidBalance < liquidityReserveRatio) {
revert BalanceBelowliquidityReserveRatio();
}

Expand Down
3 changes: 2 additions & 1 deletion src/TermAuctionList.sol
Original file line number Diff line number Diff line change
Expand Up @@ -277,8 +277,9 @@ library TermAuctionList {
for (uint256 i; i < offers.length; i++) {
PendingOfferMemory memory offer = offers[i];

// Filter by specific repo token if provided
// Filter by specific repo token if provided, address(0) bypasses this filter
if (repoTokenToMatch != address(0) && offer.repoToken != repoTokenToMatch) {
// Not a match, skip
continue;
}

Expand Down

0 comments on commit a929647

Please sign in to comment.