Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/should rebalance #2

Merged
merged 7 commits into from
Jan 6, 2025
Merged
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
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
Loading