Skip to content

Commit

Permalink
Merge pull request #2 from yieldnest/feat/shouldRebalance
Browse files Browse the repository at this point in the history
Feat/should rebalance
  • Loading branch information
dan13ram authored Jan 6, 2025
2 parents 81eb4d3 + abb2d5a commit 6b6300c
Show file tree
Hide file tree
Showing 6 changed files with 500 additions and 175 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ name: CI

on:
push:
branches:
- main
- dev
pull_request:
workflow_dispatch:

Expand Down
17 changes: 17 additions & 0 deletions .solhint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"extends": "solhint:recommended",
"plugins": [],
"rules": {
"avoid-suicide": "error",
"avoid-sha3": "warn",
"compiler-version": ["error", "^0.8.0"],
"func-visibility": ["warn", { "ignoreConstructors": true }],
"reason-string": ["warn", { "maxLength": 64 }],
"not-rely-on-time": "warn",
"state-visibility": "error",
"max-line-length": ["warn", 122],
"no-console": "off",
"func-name-mixedcase": "off",
"no-inline-assembly": "off"
}
}
2 changes: 1 addition & 1 deletion lib/yieldnest-vault
125 changes: 101 additions & 24 deletions src/BaseKeeper.sol
Original file line number Diff line number Diff line change
@@ -1,61 +1,138 @@
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.24;

// import {IVault} from "lib/yieldnest-vault/src/interface/IVault.sol";
import {Ownable} from "lib/yieldnest-vault/lib/openzeppelin-contracts/contracts/access/Ownable.sol";
import {IERC20} from "lib/yieldnest-vault/lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {IProvider} from "lib/yieldnest-vault/src/interface/IProvider.sol";

contract BaseKeeper {
import {Vault} from "lib/yieldnest-vault/src/Vault.sol";
import {IVault} from "lib/yieldnest-vault/src/interface/IVault.sol";
import {Math} from "src/libraries/Math.sol";

import {console} from "lib/yieldnest-vault/lib/forge-std/src/console.sol";

contract BaseKeeper is Ownable {
uint256[] public initialRatios;
uint256[] public finalRatios;
uint256[] public targetRatios;

// vault[0] is max vault and rest are underlying vaults
address[] public vaults;
address public asset;
IVault public maxVault;

uint256 public tolerance;

struct Transfer {
uint256 from;
uint256 to;
uint256 amount;
}

function setData(uint256[] memory _initialRatios, uint256[] memory _finalRatios, address[] memory _vaults) public {
require(_initialRatios.length > 1, "Array length must be greater than 1");
require(_initialRatios.length == _finalRatios.length, "Array lengths must match");
require(_initialRatios.length == _vaults.length, "Array lengths must match");
constructor() Ownable(msg.sender) {}

initialRatios = _initialRatios;
finalRatios = _finalRatios;
function setTolerance(uint256 _tolerance) public onlyOwner {
tolerance = _tolerance;
}

function setData(uint256[] memory _targetRatios, address[] memory _vaults) public onlyOwner {
require(_targetRatios.length > 1, "Array length must be greater than 1");
require(_targetRatios.length == _vaults.length, "Array lengths must match");

targetRatios = _targetRatios;
initialRatios = new uint256[](_targetRatios.length);
vaults = _vaults;
for (uint256 i = 0; i < _vaults.length; i++) {
require(isVault(_vaults[i]), "Invalid vault");
}

maxVault = IVault(payable(_vaults[0]));
asset = maxVault.asset();

require(_totalInitialRatios() == _totalTargetRatios(), "Initial and target ratios must match");
}

function totalInitialRatios() public view returns (uint256) {
function _totalInitialRatios() internal returns (uint256) {
uint256 totalAssets = maxVault.totalAssets();
uint256 total = 0;
for (uint256 i = 0; i < initialRatios.length; i++) {
for (uint256 i = 0; i < targetRatios.length; i++) {
initialRatios[i] = calculateCurrentRatio(vaults[i], totalAssets);
total += initialRatios[i];
}
require(total == 1e18, "Initial ratios must add up to 100 %");
return total;
}

function totalFinalRatios() public view returns (uint256) {
function _totalTargetRatios() internal view returns (uint256) {
uint256 total = 0;
for (uint256 i = 0; i < finalRatios.length; i++) {
total += finalRatios[i];
for (uint256 i = 0; i < targetRatios.length; i++) {
total += targetRatios[i];
}
return total;
}

function caculateSteps() public view returns (Transfer[] memory) {
uint256 length = initialRatios.length;
function calculateCurrentRatio(address vault, uint256 totalAssets) public view returns (uint256) {
uint256 balance;
if (vault == address(maxVault)) {
balance = IERC20(asset).balanceOf(address(maxVault));
} else {
balance = IVault(vault).totalAssets();
}

uint256 rate = IProvider(maxVault.provider()).getRate(asset);

// get current percentage in wad: ((wad*X) / Y) / WAD = percentZ (1e18 = 100%)
uint256 adjustedBalance = Math.wmul(balance, rate);
uint256 currentRatio = Math.wdiv(adjustedBalance, totalAssets);
return currentRatio;
}

function isVault(address target) public view returns (bool) {
try Vault(payable(target)).VAULT_VERSION() returns (string memory version) {
return bytes(version).length > 0;
} catch {
return false;
}
}

function shouldRebalance() public view returns (bool) {
uint256 totalAssets = maxVault.totalAssets();
address vault;

// Check each underlying asset's ratio.
for (uint256 i = 0; i < vaults.length; i++) {
vault = vaults[i];
uint256 actualRatio = calculateCurrentRatio(vault, totalAssets); // Calculate current ratio

// Check if the actual ratio deviates from the target ratio
if (!isWithinTolerance(actualRatio, targetRatios[i])) {
return true; // Rebalancing is required
}
}
// All vaults are within target ratios
return false;
}

function isWithinTolerance(uint256 actualWAD, uint256 targetWAD) public view returns (bool) {
if (actualWAD >= targetWAD) {
// todo: make tolerance a percentage
return (actualWAD - targetWAD) <= tolerance; // Upper bound
} else {
return (targetWAD - actualWAD) <= tolerance; // Lower bound
}
}

function rebalance() public returns (Transfer[] memory) {
uint256 length = targetRatios.length;
require(length > 1, "Array length must be greater than 1");
require(length == finalRatios.length, "Array lengths must match");
require(length == targetRatios.length, "Array lengths must match");
require(length == vaults.length, "Array lengths must match");

uint256 totalInitial = totalInitialRatios();
uint256 totalFinal = totalFinalRatios();
require(totalInitial == totalFinal, "Ratios must add up");
uint256 totalInitial = _totalInitialRatios();
uint256 totalFinal = _totalTargetRatios();

// address baseVault = vaults[0];
// uint256 totalAssets = IVault(baseVault).totalAssets();
require(totalInitial == totalFinal, "Ratios must add up");

uint256 totalAssets = 100; // for testing set to 100
uint256 totalAssets = maxVault.totalAssets();

uint256[] memory initialAmounts = new uint256[](length);
for (uint256 i = 0; i < length; i++) {
Expand All @@ -64,7 +141,7 @@ contract BaseKeeper {

uint256[] memory finalAmounts = new uint256[](length);
for (uint256 i = 0; i < length; i++) {
finalAmounts[i] = finalRatios[i] * totalAssets / totalFinal;
finalAmounts[i] = targetRatios[i] * totalAssets / totalFinal;
}

int256[] memory diffs = new int256[](length);
Expand Down
Loading

0 comments on commit 6b6300c

Please sign in to comment.