@@ -35,7 +35,7 @@ import { BondHelpers } from "./_utils/BondHelpers.sol";
3535 * into the reserve. At any time, the reserve holds at most 2 classes of tokens
3636 * i.e) the seniors and the underlying collateral.
3737 *
38- * Incentivized parties can "rollover" tranches approaching maturity or the underlying collateral,
38+ * The rollover vault can "rollover" tranches approaching maturity or the underlying collateral,
3939 * for newer seniors (which expire further out in the future) that belong to the updated "depositBond".
4040 *
4141 *
@@ -45,6 +45,8 @@ import { BondHelpers } from "./_utils/BondHelpers.sol";
4545 * This brings the system storage state up to date.
4646 *
4747 * CRITICAL: On the 3 main system operations: deposit, redeem and rollover;
48+ *
49+ * The system charges a fee for minting and burning perp tokens, which are paid to the vault.
4850 * We first compute fees before executing any transfers in or out of the system.
4951 * The ordering of operations is very important as the fee computation logic,
5052 * requires the system TVL as an input and which should be recorded prior to any value
@@ -57,6 +59,12 @@ import { BondHelpers } from "./_utils/BondHelpers.sol";
5759 * When computing the value of assets in the system, the code always over-values by
5860 * rounding up. When computing the value of incoming assets, the code rounds down.
5961 *
62+ * @dev Demand imbalance between perp and the vault
63+ * is restored through a "rebalancing" mechanism similar to a funding rate. When value needs to flow from perp to the vault,
64+ * the system debases the value of perp tokens by minting perp tokens to the vault.
65+ * When value needs to flow from the vault to perp, the underlying collateral tokens are
66+ * transferred from the vault into perp's reserve thereby enriching the value of perp tokens.
67+ *
6068 */
6169contract PerpetualTranche is
6270 ERC20BurnableUpgradeable ,
@@ -381,8 +389,8 @@ contract PerpetualTranche is
381389 }
382390
383391 // Calculates the fee adjusted amount of perp tokens minted when depositing `trancheInAmt` of tranche tokens
384- // NOTE: This operation should precede any token transfers.
385- uint256 perpAmtMint = _computeMintAmt (trancheIn, trancheInAmt);
392+ // NOTE: This calculation should precede any token transfers.
393+ ( uint256 perpAmtMint , uint256 perpFeeAmt ) = _computeMintAmt (trancheIn, trancheInAmt);
386394 if (trancheInAmt <= 0 || perpAmtMint <= 0 ) {
387395 return 0 ;
388396 }
@@ -393,6 +401,9 @@ contract PerpetualTranche is
393401 // mints perp tokens to the sender
394402 _mint (msg .sender , perpAmtMint);
395403
404+ // mint fees are paid to the vault by minting perp tokens.
405+ _mint (address (vault), perpFeeAmt);
406+
396407 // post-deposit checks
397408 _enforceMintCaps (trancheIn);
398409
@@ -401,19 +412,22 @@ contract PerpetualTranche is
401412
402413 /// @inheritdoc IPerpetualTranche
403414 function redeem (
404- uint256 perpAmtBurnt
415+ uint256 perpAmt
405416 ) external override afterStateUpdate nonReentrant whenNotPaused returns (TokenAmount[] memory ) {
406417 // verifies if burn amount is acceptable
407- if (perpAmtBurnt <= 0 ) {
418+ if (perpAmt <= 0 ) {
408419 return new TokenAmount [](0 );
409420 }
410421
411422 // Calculates the fee adjusted share of reserve tokens to be redeemed
412- // NOTE: This operation should precede any token transfers.
413- TokenAmount[] memory tokensOut = _computeRedemptionAmts (perpAmtBurnt );
423+ // NOTE: This calculation should precede any token transfers.
424+ ( TokenAmount[] memory tokensOut , uint256 perpFeeAmt ) = _computeRedemptionAmts (perpAmt );
414425
415426 // burns perp tokens from the sender
416- _burn (msg .sender , perpAmtBurnt);
427+ _burn (msg .sender , perpAmt - perpFeeAmt);
428+
429+ // redemption fees are paid to transferring perp tokens
430+ transfer (address (vault), perpFeeAmt);
417431
418432 // transfers reserve tokens out
419433 uint8 tokensOutCount = uint8 (tokensOut.length );
@@ -437,8 +451,8 @@ contract PerpetualTranche is
437451 revert UnacceptableRollover ();
438452 }
439453
440- // Calculates the fee adjusted amount of tranches exchanged during a rolled over
441- // NOTE: This operation should precede any token transfers.
454+ // Calculates the amount of tranches exchanged during a rolled over
455+ // NOTE: This calculation should precede any token transfers.
442456 RolloverData memory r = _computeRolloverAmt (trancheIn, tokenOut, trancheInAmtAvailable);
443457
444458 // Verifies if rollover amount is acceptable
@@ -455,6 +469,15 @@ contract PerpetualTranche is
455469 return r;
456470 }
457471
472+ /// @inheritdoc IPerpetualTranche
473+ /// @dev CAUTION: Only the whitelisted vault can call this function.
474+ /// The logic controlling the frequency and magnitude of debasement should be vetted.
475+ function rebalanceToVault (
476+ uint256 underlyingAmtToTransfer
477+ ) external override onlyVault afterStateUpdate nonReentrant whenNotPaused {
478+ _mint (address (vault), underlyingAmtToTransfer.mulDiv (totalSupply (), _reserveValue ()));
479+ }
480+
458481 /// @inheritdoc IPerpetualTranche
459482 function getDepositBond () external override afterStateUpdate returns (IBondController) {
460483 return _depositBond;
@@ -564,14 +587,14 @@ contract PerpetualTranche is
564587 if (! _isDepositTranche (trancheIn)) {
565588 revert UnexpectedAsset ();
566589 }
567- return _computeMintAmt (trancheIn, trancheInAmt);
590+ (uint256 perpAmtMint , ) = _computeMintAmt (trancheIn, trancheInAmt);
591+ return perpAmtMint;
568592 }
569593
570594 /// @inheritdoc IPerpetualTranche
571- function computeRedemptionAmts (
572- uint256 perpAmtBurnt
573- ) external override afterStateUpdate returns (TokenAmount[] memory ) {
574- return _computeRedemptionAmts (perpAmtBurnt);
595+ function computeRedemptionAmts (uint256 perpAmt ) external override afterStateUpdate returns (TokenAmount[] memory ) {
596+ (TokenAmount[] memory tokensOut , ) = _computeRedemptionAmts (perpAmt);
597+ return tokensOut;
575598 }
576599
577600 /// @inheritdoc IPerpetualTranche
@@ -724,7 +747,10 @@ contract PerpetualTranche is
724747 }
725748
726749 /// @dev Computes the fee adjusted perp mint amount for given amount of tranche tokens deposited into the reserve.
727- function _computeMintAmt (ITranche trancheIn , uint256 trancheInAmt ) private view returns (uint256 ) {
750+ function _computeMintAmt (
751+ ITranche trancheIn ,
752+ uint256 trancheInAmt
753+ ) private view returns (uint256 perpAmtMint , uint256 perpFeeAmt ) {
728754 uint256 valueIn = _computeReserveTrancheValue (
729755 trancheIn,
730756 _depositBond,
@@ -740,68 +766,56 @@ contract PerpetualTranche is
740766
741767 // Compute mint amt
742768 uint256 perpSupply = totalSupply ();
743- uint256 perpAmtMint = valueIn;
769+ perpAmtMint = valueIn;
744770 if (perpSupply > 0 ) {
745771 perpAmtMint = perpAmtMint.mulDiv (perpSupply, _reserveValue ());
746772 }
747773
748- // The mint fees are settled by simply minting fewer perps.
774+ // Compute the fee amount
749775 if (feePerc > 0 ) {
750- perpAmtMint = perpAmtMint.mulDiv (ONE - feePerc, ONE);
776+ perpFeeAmt = perpAmtMint.mulDiv (feePerc, ONE, MathUpgradeable.Rounding.Up);
777+ perpAmtMint -= perpFeeAmt;
751778 }
752-
753- return perpAmtMint;
754779 }
755780
756781 /// @dev Computes the reserve token amounts redeemed when a given number of perps are burnt.
757- function _computeRedemptionAmts (uint256 perpAmtBurnt ) private view returns (TokenAmount[] memory ) {
782+ function _computeRedemptionAmts (
783+ uint256 perpAmt
784+ ) private view returns (TokenAmount[] memory reserveTokens , uint256 perpFeeAmt ) {
758785 uint256 perpSupply = totalSupply ();
759786
760787 //-----------------------------------------------------------------------------
761788 // We charge no burn fee when interacting with other parts of the system.
762789 uint256 feePerc = _isProtocolCaller () ? 0 : feePolicy.computePerpBurnFeePerc ();
763790 //-----------------------------------------------------------------------------
764791
792+ // Compute the fee amount
793+ if (feePerc > 0 ) {
794+ perpFeeAmt = perpAmt.mulDiv (feePerc, ONE, MathUpgradeable.Rounding.Up);
795+ perpAmt -= perpFeeAmt;
796+ }
797+
765798 // Compute redemption amounts
766799 uint8 reserveCount = uint8 (_reserves.length ());
767- TokenAmount[] memory reserveTokens = new TokenAmount [](reserveCount);
800+ reserveTokens = new TokenAmount [](reserveCount);
768801 for (uint8 i = 0 ; i < reserveCount; ++ i) {
769802 IERC20Upgradeable tokenOut = _reserveAt (i);
770803 reserveTokens[i] = TokenAmount ({
771804 token: tokenOut,
772- amount: tokenOut.balanceOf (address (this )).mulDiv (perpAmtBurnt , perpSupply)
805+ amount: tokenOut.balanceOf (address (this )).mulDiv (perpAmt , perpSupply)
773806 });
774-
775- // The burn fees are settled by simply redeeming for fewer tranches.
776- if (feePerc > 0 ) {
777- reserveTokens[i].amount = reserveTokens[i].amount.mulDiv (ONE - feePerc, ONE);
778- }
779807 }
780808
781- return (reserveTokens);
809+ return (reserveTokens, perpFeeAmt );
782810 }
783811
784812 /// @dev Computes the amount of reserve tokens that can be rolled out for the given amount of tranches deposited.
785- /// The relative ratios of tokens In/Out are adjusted based on the current rollover fee perc.
786813 function _computeRolloverAmt (
787814 ITranche trancheIn ,
788815 IERC20Upgradeable tokenOut ,
789816 uint256 trancheInAmtAvailable
790817 ) private view returns (RolloverData memory ) {
791818 //-----------------------------------------------------------------------------
792- // The rollover fees are settled by, adjusting the exchange rate
793- // between `trancheInAmt` and `tokenOutAmt`.
794- //
795- int256 feePerc = feePolicy.computePerpRolloverFeePerc (
796- feePolicy.computeDeviationRatio (
797- SubscriptionParams ({
798- perpTVL: _reserveValue (),
799- vaultTVL: vault.getTVL (),
800- seniorTR: _depositBond.getSeniorTrancheRatio ()
801- })
802- )
803- );
804- //-----------------------------------------------------------------------------
805819
806820 // We compute "price" as the value of a unit token.
807821 // The perp, tranche tokens and the underlying are denominated as fixed point numbers
@@ -832,8 +846,8 @@ contract PerpetualTranche is
832846 return RolloverData ({ trancheInAmt: 0 , tokenOutAmt: 0 });
833847 }
834848 //-----------------------------------------------------------------------------
835- // Basic rollover with fees :
836- // (1 +/- f) . ( trancheInAmt . trancheInPrice) = (tokenOutAmt . tokenOutPrice)
849+ // Basic rollover:
850+ // (trancheInAmt . trancheInPrice) = (tokenOutAmt . tokenOutPrice)
837851 //-----------------------------------------------------------------------------
838852
839853 // Using perp's tokenOutBalance, we calculate the amount of tokens in to rollover
@@ -844,17 +858,6 @@ contract PerpetualTranche is
844858 trancheInAmt: tokenOutBalance.mulDiv (tokenOutPrice, trancheInPrice, MathUpgradeable.Rounding.Up)
845859 });
846860
847- // A positive fee percentage implies that perp charges rotators by
848- // offering tranchesOut for a premium, i.e) more tranches in.
849- if (feePerc > 0 ) {
850- r.trancheInAmt = r.trancheInAmt.mulDiv (ONE, ONE - feePerc.toUint256 (), MathUpgradeable.Rounding.Up);
851- }
852- // A negative fee percentage (or a reward) implies that perp pays the rotators by
853- // offering tranchesOut at a discount, i.e) fewer tranches in.
854- else if (feePerc < 0 ) {
855- r.trancheInAmt = r.trancheInAmt.mulDiv (ONE, ONE + feePerc.abs (), MathUpgradeable.Rounding.Up);
856- }
857-
858861 //-----------------------------------------------------------------------------
859862
860863 // When the trancheInAmt exceeds trancheInAmtAvailable:
@@ -864,19 +867,6 @@ contract PerpetualTranche is
864867 // Given the amount of tranches In, we compute the amount of tokens out
865868 r.trancheInAmt = trancheInAmtAvailable;
866869 r.tokenOutAmt = trancheInAmtAvailable.mulDiv (trancheInPrice, tokenOutPrice);
867-
868- // A positive fee percentage implies that perp charges rotators by
869- // accepting tranchesIn at a discount, i.e) fewer tokens out.
870- // This results in perp enrichment.
871- if (feePerc > 0 ) {
872- r.tokenOutAmt = r.tokenOutAmt.mulDiv (ONE - feePerc.abs (), ONE);
873- }
874- // A negative fee percentage (or a reward) implies that perp pays the rotators by
875- // accepting tranchesIn at a premium, i.e) more tokens out.
876- // This results in perp debasement.
877- else if (feePerc < 0 ) {
878- r.tokenOutAmt = r.tokenOutAmt.mulDiv (ONE + feePerc.abs (), ONE);
879- }
880870 }
881871
882872 return r;
0 commit comments