Skip to content

Commit

Permalink
rebalancing functionality with consideration for idle liquidity
Browse files Browse the repository at this point in the history
  • Loading branch information
mcclurejt committed Sep 10, 2024
1 parent 60ec1f1 commit 044614c
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 19 deletions.
69 changes: 53 additions & 16 deletions contracts/Everlong.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { SafeCast } from "hyperdrive/contracts/src/libraries/SafeCast.sol";
import { ERC20 } from "openzeppelin/token/ERC20/ERC20.sol";
import { SafeERC20 } from "openzeppelin/token/ERC20/utils/SafeERC20.sol";
import { IEverlong } from "./interfaces/IEverlong.sol";
import { EVERLONG_KIND, EVERLONG_VERSION } from "./libraries/Constants.sol";
import { EVERLONG_KIND, EVERLONG_VERSION, ONE } from "./libraries/Constants.sol";
import { HyperdriveExecutionLibrary } from "./libraries/HyperdriveExecution.sol";
import { Portfolio } from "./libraries/Portfolio.sol";

Expand Down Expand Up @@ -81,10 +81,6 @@ contract Everlong is IEverlong {

// โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Immutables โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

// NOTE: Immutables accessed during transactions are left as internal
// to avoid the gas overhead of auto-generated getter functions.
// https://zokyo-auditing-tutorials.gitbook.io/zokyo-gas-savings/tutorials/gas-saving-technique-23-public-to-private-constants

/// @dev Name of the Everlong token.
string public _name;

Expand All @@ -104,6 +100,12 @@ contract Everlong is IEverlong {
/// @dev Decimals to use with asset.
uint8 internal immutable _decimals;

/// @notice Target percentage of assets to leave uninvested.
uint256 public immutable targetIdleLiquidityPercentage;

/// @notice Maximum percentage of assets to leave uninvested.
uint256 public immutable maxIdleLiquidityPercentage;

/// @dev Kind of everlong.
string public constant override kind = EVERLONG_KIND;

Expand Down Expand Up @@ -148,12 +150,18 @@ contract Everlong is IEverlong {
/// @param __decimals Decimals of the Everlong token and Hyperdrive token.
/// @param _hyperdrive Address of the Hyperdrive instance.
/// @param _asBase Whether to use the base or shares token from Hyperdrive.
/// @param _targetIdleLiquidityPercentage Target percentage of funds to
/// keep idle.
/// @param _maxIdleLiquidityPercentage Max percentage of funds to keep
/// idle.
constructor(
string memory __name,
string memory __symbol,
uint8 __decimals,
address _hyperdrive,
bool _asBase
bool _asBase,
uint256 _targetIdleLiquidityPercentage,
uint256 _maxIdleLiquidityPercentage
) {
// Store constructor parameters.
_name = __name;
Expand All @@ -164,6 +172,8 @@ contract Everlong is IEverlong {
_asset = _asBase
? IHyperdrive(_hyperdrive).baseToken()
: IHyperdrive(_hyperdrive).vaultSharesToken();
targetIdleLiquidityPercentage = _targetIdleLiquidityPercentage;
maxIdleLiquidityPercentage = _maxIdleLiquidityPercentage;

// Set the admin to the contract deployer.
admin = msg.sender;
Expand Down Expand Up @@ -197,8 +207,8 @@ contract Everlong is IEverlong {
}

// Estimate the value of everlong-controlled positions by calculating
// the proceeds one would receive from closing a position with the portfolio's
// total amount of bonds and weighted average maturity.
// the proceeds one would receive from closing a position with the
// portfolio's total amount of bonds and weighted average maturity.
// The weighted average maturity is rounded to the next checkpoint
// timestamp to underestimate the value.
return
Expand All @@ -215,9 +225,9 @@ contract Everlong is IEverlong {
);
}

/// @dev Rebalance after a deposit if needed.
/// @dev Attempt rebalancing after a deposit if idle is above max.
function _afterDeposit(uint256, uint256) internal virtual override {
if (canRebalance()) {
if (ERC20(_asset).balanceOf(address(this)) > maxIdleLiquidity()) {
rebalance();
}
}
Expand All @@ -239,6 +249,11 @@ contract Everlong is IEverlong {
/// @notice Rebalance the everlong portfolio by closing mature positions
/// and using the proceeds to open new positions.
function rebalance() public override {
// Early return if no rebalancing is needed.
if (!canRebalance()) {
return;
}

// Close matured positions.
_closeMaturedPositions();

Expand All @@ -253,12 +268,32 @@ contract Everlong is IEverlong {
_portfolio.handleOpenPosition(maturityTime, bondAmount);
}

// FIXME: Consider idle liquidity + maybe maxLong?
//
/// @notice Returns whether the portfolio needs rebalancing.
/// @return True if the portfolio needs rebalancing, false otherwise.
/// @notice Returns true if the portfolio can be rebalanced.
/// @notice The portfolio can be rebalanced if:
/// - Any positions are matured.
/// - The current idle liquidity is above the target.
/// @return True if the portfolio can be rebalanced, false otherwise.
function canRebalance() public view returns (bool) {
return true;
return
(!_portfolio.isEmpty() &&
IHyperdrive(hyperdrive).isMature(_portfolio.head())) ||
ERC20(_asset).balanceOf(address(this)) > targetIdleLiquidity();
}

// TODO: Use cached poolconfig
function targetIdleLiquidity() public view returns (uint256 assets) {
assets = targetIdleLiquidityPercentage
.mulDivDown(totalAssets(), ONE)
.max(
IHyperdrive(hyperdrive).getPoolConfig().minimumTransactionAmount
);
}

// TODO: Use cached poolconfig
function maxIdleLiquidity() public view returns (uint256 assets) {
assets = maxIdleLiquidityPercentage.mulDivDown(totalAssets(), ONE).max(
IHyperdrive(hyperdrive).getPoolConfig().minimumTransactionAmount
);
}

// โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
Expand Down Expand Up @@ -340,7 +375,9 @@ contract Everlong is IEverlong {
/// @notice Returns whether the portfolio has matured positions.
/// @return True if the portfolio has matured positions, false otherwise.
function hasMaturedPositions() external view returns (bool) {
return IHyperdrive(hyperdrive).isMature(_portfolio.head());
return
!_portfolio.isEmpty() &&
IHyperdrive(hyperdrive).isMature(_portfolio.head());
}

/// @notice Retrieve the position at the specified location in the queue..
Expand Down
16 changes: 14 additions & 2 deletions test/exposed/EverlongExposed.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,20 @@ contract EverlongExposed is Everlong, Test {
string memory symbol_,
uint8 decimals_,
address hyperdrive_,
bool asBase_
) Everlong(name_, symbol_, decimals_, hyperdrive_, asBase_) {}
bool asBase_,
uint256 _targetIdleLiquidityPercentage,
uint256 _maxIdleLiquidityPercentage
)
Everlong(
name_,
symbol_,
decimals_,
hyperdrive_,
asBase_,
_targetIdleLiquidityPercentage,
_maxIdleLiquidityPercentage
)
{}

// โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
// โ”‚ ERC4626 โ”‚
Expand Down
7 changes: 6 additions & 1 deletion test/harnesses/EverlongTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ contract EverlongTest is HyperdriveTest, IEverlongEvents {
/// @dev Everlong token symbol.
string internal EVERLONG_SYMBOL = "evTest";

uint256 internal TARGET_IDLE_LIQUIDITY_PERCENTAGE = 0;
uint256 internal MAX_IDLE_LIQUIDITY_PERCENTAGE = 0;

// โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
// โ”‚ Hyperdrive Configuration โ”‚
// โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
Expand Down Expand Up @@ -95,7 +98,9 @@ contract EverlongTest is HyperdriveTest, IEverlongEvents {
EVERLONG_SYMBOL,
hyperdrive.decimals(),
address(hyperdrive),
true
true,
TARGET_IDLE_LIQUIDITY_PERCENTAGE,
MAX_IDLE_LIQUIDITY_PERCENTAGE
);
vm.stopPrank();

Expand Down

0 comments on commit 044614c

Please sign in to comment.