diff --git a/.gitmodules b/.gitmodules index 7712b40..226e7af 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "lib/yieldnest-vault"] path = lib/yieldnest-vault url = https://github.com/yieldnest/yieldnest-vault - branch = eth-max-vault + branch = bnb-max-vault diff --git a/lib/yieldnest-vault b/lib/yieldnest-vault index a9764a5..74d53ec 160000 --- a/lib/yieldnest-vault +++ b/lib/yieldnest-vault @@ -1 +1 @@ -Subproject commit a9764a5b7e489f10b92501cd66acb4fa05c347fe +Subproject commit 74d53eccc291e1fcced590b40dcff62e2ad98653 diff --git a/src/BaseKeeper.sol b/src/BaseKeeper.sol index 795eb05..9b0a012 100644 --- a/src/BaseKeeper.sol +++ b/src/BaseKeeper.sol @@ -11,9 +11,10 @@ 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 targetRatios; +import {ReentrancyGuard} from "lib/openzeppelin-contracts/contracts/utils/ReentrancyGuard.sol"; + +contract BaseKeeper is Ownable, ReentrancyGuard { + uint256[] private targetRatios; // vault[0] is max vault and rest are underlying vaults address[] public vaults; @@ -22,12 +23,16 @@ contract BaseKeeper is Ownable { uint256 public tolerance; - struct Transfer { - uint256 from; + struct Deposit { uint256 to; uint256 amount; } + struct Withdraw { + uint256 from; + uint256 amount; + } + constructor() Ownable(msg.sender) {} function setTolerance(uint256 _tolerance) public onlyOwner { @@ -39,7 +44,6 @@ contract BaseKeeper is Ownable { 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"); @@ -48,25 +52,25 @@ contract BaseKeeper is Ownable { maxVault = IVault(payable(_vaults[0])); asset = maxVault.asset(); - require(_totalInitialRatios() == _totalTargetRatios(), "Initial and target ratios must match"); + require(totalInitialRatios() == totalTargetRatios(), "Initial and target ratios must match"); } - function _totalInitialRatios() internal returns (uint256) { + function totalInitialRatios() public view returns (uint256) { uint256 totalAssets = maxVault.totalAssets(); uint256 total = 0; for (uint256 i = 0; i < targetRatios.length; i++) { - initialRatios[i] = calculateCurrentRatio(vaults[i], totalAssets); - total += initialRatios[i]; + total += calculateCurrentRatio(vaults[i], totalAssets); } require(total == 1e18, "Initial ratios must add up to 100 %"); return total; } - function _totalTargetRatios() internal view returns (uint256) { + function totalTargetRatios() public view returns (uint256) { uint256 total = 0; for (uint256 i = 0; i < targetRatios.length; i++) { total += targetRatios[i]; } + require(total == 1e18, "Target ratios must add up to 100 %"); return total; } @@ -96,12 +100,10 @@ contract BaseKeeper is Ownable { 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 + uint256 actualRatio = calculateCurrentRatio(vaults[i], totalAssets); // Calculate current ratio // Check if the actual ratio deviates from the target ratio if (!isWithinTolerance(actualRatio, targetRatios[i])) { @@ -114,25 +116,41 @@ contract BaseKeeper is Ownable { function isWithinTolerance(uint256 actualWAD, uint256 targetWAD) public view returns (bool) { if (actualWAD >= targetWAD) { - // todo: make tolerance a percentage + // 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 == targetRatios.length, "Array lengths must match"); - require(length == vaults.length, "Array lengths must match"); + function currentRatios() public view returns (uint256[] memory ratios) { + uint256 length = vaults.length; + uint256 totalAssets = maxVault.totalAssets(); + ratios = new uint256[](length); + for (uint256 i = 0; i < length; i++) { + ratios[i] = calculateCurrentRatio(vaults[i], totalAssets); + } + } - uint256 totalInitial = _totalInitialRatios(); - uint256 totalFinal = _totalTargetRatios(); + function finalRatios() public view returns (uint256[] memory ratios) { + return targetRatios; + } - require(totalInitial == totalFinal, "Ratios must add up"); + function calculateDiffs() public view returns (int256[] memory diffs) { + uint256 length = vaults.length; uint256 totalAssets = maxVault.totalAssets(); + uint256[] memory initialRatios = new uint256[](length); + + uint256 totalInitial = 0; + uint256 totalFinal = totalTargetRatios(); + + for (uint256 i = 0; i < vaults.length; i++) { + initialRatios[i] = calculateCurrentRatio(vaults[i], totalAssets); + totalInitial += initialRatios[i]; + } + + require(totalInitial == totalFinal, "Ratios must add up"); uint256[] memory initialAmounts = new uint256[](length); for (uint256 i = 0; i < length; i++) { @@ -144,22 +162,38 @@ contract BaseKeeper is Ownable { finalAmounts[i] = targetRatios[i] * totalAssets / totalFinal; } - int256[] memory diffs = new int256[](length); + diffs = new int256[](length); // Calculate differences for (uint256 i = 0; i < length; i++) { diffs[i] = int256(initialAmounts[i]) - int256(finalAmounts[i]); } + } + + function calculateTransfers() public view returns (Withdraw[] memory, Deposit[] memory) { + uint256 length = vaults.length; + + int256[] memory diffs = calculateDiffs(); - Transfer[] memory steps = new Transfer[](length); - uint256 stepIndex = 0; + Withdraw[] memory withdraws = new Withdraw[](length); + Deposit[] memory deposits = new Deposit[](length); + uint256 withdrawIndex = 0; + uint256 depositIndex = 0; for (uint256 i = 0; i < length; i++) { if (diffs[i] > 0) { for (uint256 j = 0; j < length; j++) { if (diffs[j] < 0) { uint256 transferAmount = uint256(diffs[i] > -diffs[j] ? -diffs[j] : diffs[i]); - steps[stepIndex++] = Transfer(i, j, transferAmount); + + if (j == 0) { + withdraws[withdrawIndex++] = Withdraw(i, transferAmount); + } else if (i == 0) { + deposits[depositIndex++] = Deposit(j, transferAmount); + } else { + withdraws[withdrawIndex++] = Withdraw(i, transferAmount); + deposits[depositIndex++] = Deposit(j, transferAmount); + } diffs[i] -= int256(transferAmount); diffs[j] += int256(transferAmount); @@ -170,11 +204,89 @@ contract BaseKeeper is Ownable { } } - Transfer[] memory finalSteps = new Transfer[](stepIndex); - for (uint256 i = 0; i < stepIndex; i++) { - finalSteps[i] = steps[i]; + Withdraw[] memory sortedWithdraws = new Withdraw[](length); + Deposit[] memory sortedDeposits = new Deposit[](length); + + uint256 sortedWithdrawIndex = 0; + uint256 sortedDepositIndex = 0; + + for (uint256 j = 0; j < length; j++) { + uint256 withdrawAmount = 0; + uint256 depositAmount = 0; + for (uint256 i = 0; i < withdrawIndex; i++) { + if (withdraws[i].from == j) { + withdrawAmount += withdraws[i].amount; + } + } + for (uint256 i = 0; i < depositIndex; i++) { + if (deposits[i].to == j) { + depositAmount += deposits[i].amount; + } + } + if (withdrawAmount > 0) { + sortedWithdraws[sortedWithdrawIndex++] = Withdraw(j, withdrawAmount); + } + if (depositAmount > 0) { + sortedDeposits[sortedDepositIndex++] = Deposit(j, depositAmount); + } + } + + Withdraw[] memory finalWithdraws = new Withdraw[](sortedWithdrawIndex); + Deposit[] memory finalDeposits = new Deposit[](sortedDepositIndex); + + for (uint256 i = 0; i < sortedWithdrawIndex; i++) { + finalWithdraws[i] = sortedWithdraws[i]; + } + for (uint256 i = 0; i < sortedDepositIndex; i++) { + finalDeposits[i] = sortedDeposits[i]; + } + + return (finalWithdraws, finalDeposits); + } + + function rebalance() public nonReentrant { + (Withdraw[] memory withdraws, Deposit[] memory deposits) = calculateTransfers(); + for (uint256 i = 0; i < withdraws.length; i++) { + _executeWithdraw(withdraws[i]); + } + for (uint256 i = 0; i < deposits.length; i++) { + _executeDeposit(deposits[i]); } + } + + function _executeWithdraw(Withdraw memory withdraw) internal { + uint256 amount = withdraw.amount; + address vault = vaults[withdraw.from]; + + address[] memory targets = new address[](1); + targets[0] = vault; + + uint256[] memory values = new uint256[](1); + values[0] = 0; + + bytes[] memory data = new bytes[](1); + data[0] = + abi.encodeWithSignature("withdraw(uint256,address,address)", amount, address(maxVault), address(maxVault)); + + maxVault.processor(targets, values, data); + } + + function _executeDeposit(Deposit memory deposit) internal { + uint256 amount = deposit.amount; + address vault = vaults[deposit.to]; + + address[] memory targets = new address[](2); + targets[0] = asset; + targets[1] = vault; + + uint256[] memory values = new uint256[](2); + values[0] = 0; + values[1] = 0; + + bytes[] memory data = new bytes[](2); + data[0] = abi.encodeWithSignature("approve(address,uint256)", vault, amount); + data[1] = abi.encodeWithSignature("deposit(uint256,address)", amount, address(maxVault)); - return finalSteps; + maxVault.processor(targets, values, data); } } diff --git a/test/unit/keeper.t.sol b/test/unit/keeper.t.sol index 945ff0f..3f595af 100644 --- a/test/unit/keeper.t.sol +++ b/test/unit/keeper.t.sol @@ -1,14 +1,18 @@ // SPDX-License-Identifier: BSD-3-Clause + pragma solidity ^0.8.24; import "lib/forge-std/src/Test.sol"; import {MainnetActors} from "lib/yieldnest-vault/script/Actors.sol"; -import {SetupVault, Vault, WETH9} from "lib/yieldnest-vault/test/unit/helpers/SetupVault.sol"; +import {MainnetContracts as MC} from "lib/yieldnest-vault/script/Contracts.sol"; + +import {VaultUtils} from "lib/yieldnest-vault/script/VaultUtils.sol"; +import {IVault, SetupVault, Vault, WETH9} from "lib/yieldnest-vault/test/unit/helpers/SetupVault.sol"; import {BaseKeeper} from "src/BaseKeeper.sol"; -contract BaseKeeperTest is Test, MainnetActors { - BaseKeeper public baseKeeper; +contract BaseKeeperTest is Test, MainnetActors, VaultUtils { + BaseKeeper public keeper; Vault public maxVault; Vault public underlyingVault1; Vault public underlyingVault2; @@ -20,18 +24,44 @@ contract BaseKeeperTest is Test, MainnetActors { address public alice = address(0xa11ce); function setUp() public { - baseKeeper = new BaseKeeper(); + keeper = new BaseKeeper(); SetupVault setupVault = new SetupVault(); (maxVault, weth) = setupVault.setup(); (underlyingVault1,) = setupVault.setup(); (underlyingVault2,) = setupVault.setup(); + vm.startPrank(ADMIN); + maxVault.grantRole(maxVault.PROCESSOR_ROLE(), address(keeper)); + vm.stopPrank(); + vm.startPrank(ASSET_MANAGER); maxVault.addAsset(address(underlyingVault1), false); maxVault.addAsset(address(underlyingVault2), false); vm.stopPrank(); + address[] memory vaults = new address[](2); + vaults[0] = address(underlyingVault1); + vaults[1] = address(underlyingVault2); + + // set rules for maxVault + vm.startPrank(PROCESSOR_MANAGER); + setDepositRule(maxVault, address(underlyingVault1)); + setDepositRule(maxVault, address(underlyingVault2)); + + setApprovalRule(maxVault, address(weth), vaults); + + setWithdrawRule(maxVault, address(underlyingVault1)); + setWithdrawRule(maxVault, address(underlyingVault2)); + + // set rules for underlyingVaults + setDepositRule(underlyingVault1, MC.BUFFER); + setDepositRule(underlyingVault2, MC.BUFFER); + setApprovalRule(underlyingVault1, address(weth), MC.BUFFER); + setApprovalRule(underlyingVault2, address(weth), MC.BUFFER); + + vm.stopPrank(); + // Give Alice some tokens deal(alice, INITIAL_BALANCE); @@ -47,7 +77,7 @@ contract BaseKeeperTest is Test, MainnetActors { vm.label(address(maxVault), "Max Vault"); vm.label(address(underlyingVault1), "Underlying Vault 1"); vm.label(address(underlyingVault2), "Underlying Vault 2"); - vm.label(address(baseKeeper), "BaseKeeper"); + vm.label(address(keeper), "BaseKeeper"); } function test_ViewFunctions() public view { @@ -71,65 +101,153 @@ contract BaseKeeperTest is Test, MainnetActors { vaults[1] = address(underlyingVault1); vaults[2] = address(underlyingVault2); - baseKeeper.setData(finalRatios, vaults); + keeper.setData(finalRatios, vaults); - assertEq(baseKeeper.initialRatios(0), 1e18); - assertEq(baseKeeper.targetRatios(1), 40 * 1e16); - assertEq(baseKeeper.vaults(2), address(underlyingVault2)); + uint256[] memory initialRatios = keeper.currentRatios(); + uint256[] memory targetRatios = keeper.finalRatios(); + + assertEq(initialRatios.length, 3); + assertEq(targetRatios.length, 3); + + assertEq(initialRatios[0], 1e18); + assertEq(targetRatios[1], 40 * 1e16); + assertEq(keeper.vaults(2), address(underlyingVault2)); + } + + function test_CalculateTransfers_ExampleOne() public { + _setData(0.5e18, 0.25e18, 0.25e18); + + (BaseKeeper.Withdraw[] memory withdraws, BaseKeeper.Deposit[] memory deposits) = keeper.calculateTransfers(); + + assertEq(withdraws.length, 0, "Expected 0 withdraws"); + assertEq(deposits.length, 2, "Expected 2 deposits"); + + // Validate first deposit + assertEq(deposits[0].to, 1, "Expected to for deposit 0"); + assertEq(deposits[0].amount, INITIAL_BALANCE / 4, "Expected amount for deposit 0"); + + // Validate second deposit + assertEq(deposits[1].to, 2, "Expected to for deposit 1"); + assertEq(deposits[1].amount, INITIAL_BALANCE / 4, "Expected amount for deposit 1"); } function test_Rebalance_ExampleOne() public { - setData(0.5e18, 0.25e18, 0.25e18); + _setData(0.5e18, 0.25e18, 0.25e18); + _rebalanceAndValidate(); + } + + function test_CalculateTransfers_ExampleTwo() public { + _setData(0.5e18, 0.4e18, 0.1e18); - BaseKeeper.Transfer[] memory steps = baseKeeper.rebalance(); + (BaseKeeper.Withdraw[] memory withdraws, BaseKeeper.Deposit[] memory deposits) = keeper.calculateTransfers(); - assertEq(steps.length, 2, "Expected 2 steps"); + assertEq(withdraws.length, 0, "Expected 0 withdraws"); + assertEq(deposits.length, 2, "Expected 2 deposits"); - // Validate first step - assertEq(steps[0].from, 0, "Expected from for step 0"); - assertEq(steps[0].to, 1, "Expected to for step 0"); - assertEq(steps[0].amount, INITIAL_BALANCE / 4, "Expected amount for step 0"); + // Validate first deposit + assertEq(deposits[0].to, 1, "Expected to for deposit 0"); + assertEq(deposits[0].amount, 4 * INITIAL_BALANCE / 10, "Expected amount for deposit 0"); - // Validate second step - assertEq(steps[1].from, 0, "Expected from for step 1"); - assertEq(steps[1].to, 2, "Expected to for step 1"); - assertEq(steps[1].amount, INITIAL_BALANCE / 4, "Expected amount for step 1"); + // Validate second deposit + assertEq(deposits[1].to, 2, "Expected to for deposit 1"); + assertEq(deposits[1].amount, 1 * INITIAL_BALANCE / 10, "Expected amount for deposit 1"); } function test_Rebalance_ExampleTwo() public { - setData(0.5e18, 0.4e18, 0.1e18); + _setData(0.5e18, 0.4e18, 0.1e18); + _rebalanceAndValidate(); + } + + function test_CalculateTransfers_ExampleThree() public { + _setData(0.5e18, 0.2e18, 0.3e18); - BaseKeeper.Transfer[] memory steps = baseKeeper.rebalance(); + (BaseKeeper.Withdraw[] memory withdraws, BaseKeeper.Deposit[] memory deposits) = keeper.calculateTransfers(); - assertEq(steps.length, 2); + assertEq(withdraws.length, 0, "Expected 0 withdraws"); + assertEq(deposits.length, 2, "Expected 2 deposits"); - // Validate first step - assertEq(steps[0].from, 0, "Expected from for step 0"); - assertEq(steps[0].to, 1, "Expected to for step 0"); - assertEq(steps[0].amount, 4 * INITIAL_BALANCE / 10, "Expected amount for step 0"); + // Validate first deposit + assertEq(deposits[0].to, 1, "Expected to for deposit 0"); + assertEq(deposits[0].amount, 2 * INITIAL_BALANCE / 10, "Expected amount for deposit 0"); - // Validate second step - assertEq(steps[1].from, 0, "Expected from for step 1"); - assertEq(steps[1].to, 2, "Expected to for step 1"); - assertEq(steps[1].amount, 1 * INITIAL_BALANCE / 10, "Expected amount for step 1"); + // Validate second deposit + assertEq(deposits[1].to, 2, "Expected to for deposit 1"); + assertEq(deposits[1].amount, 3 * INITIAL_BALANCE / 10, "Expected amount for deposit 1"); } function test_Rebalance_ExampleThree() public { - setData(0.5e18, 0.2e18, 0.3e18); + _setData(0.5e18, 0.2e18, 0.3e18); + _rebalanceAndValidate(); + } + + function test_Rebalance_TwoTimes() public { + _setData(0.5e18, 0.2e18, 0.3e18); + _rebalanceAndValidate(); + + _setData(0.5e18, 0.4e18, 0.1e18); + + uint256[] memory currentRatios = keeper.currentRatios(); + uint256[] memory finalRatios = keeper.finalRatios(); + + assertEq(currentRatios[0], 0.5e18); + assertEq(currentRatios[1], 0.2e18); + assertEq(currentRatios[2], 0.3e18); - BaseKeeper.Transfer[] memory steps = baseKeeper.rebalance(); + assertEq(finalRatios[0], 0.5e18); + assertEq(finalRatios[1], 0.4e18); + assertEq(finalRatios[2], 0.1e18); - assertEq(steps.length, 2, "Expected 2 steps"); + (BaseKeeper.Withdraw[] memory withdraws, BaseKeeper.Deposit[] memory deposits) = keeper.calculateTransfers(); - // Validate first step - assertEq(steps[0].from, 0, "Expected from for step 0"); - assertEq(steps[0].to, 1, "Expected to for step 0"); - assertEq(steps[0].amount, 2 * INITIAL_BALANCE / 10, "Expected amount for step 0"); + assertEq(withdraws.length, 1, "Expected 1 withdraws"); + assertEq(deposits.length, 1, "Expected 1 deposits"); - // Validate second step - assertEq(steps[1].from, 0, "Expected from for step 1"); - assertEq(steps[1].to, 2, "Expected to for step 1"); - assertEq(steps[1].amount, 3 * INITIAL_BALANCE / 10, "Expected amount for step 1"); + // Validate first deposit + assertEq(deposits[0].to, 1, "Expected to for deposit 0"); + assertEq(deposits[0].amount, 2 * INITIAL_BALANCE / 10, "Expected amount for deposit 0"); + + assertEq(withdraws[0].from, 2, "Expected from for withdraw 0"); + assertEq(withdraws[0].amount, 2 * INITIAL_BALANCE / 10, "Expected amount for withdraw 0"); + + _allocateBalanceToBuffer(underlyingVault1); + _allocateBalanceToBuffer(underlyingVault2); + + _rebalanceAndValidate(); + } + + function test_Rebalance_MultipleTimes() public { + _setData(0.5e18, 0.2e18, 0.3e18); + _rebalanceAndValidate(); + + _setData(0.5e18, 0.4e18, 0.1e18); + _allocateBalanceToBuffer(underlyingVault1); + _allocateBalanceToBuffer(underlyingVault2); + + _rebalanceAndValidate(); + + _setData(0.5e18, 0.2e18, 0.3e18); + _allocateBalanceToBuffer(underlyingVault1); + _allocateBalanceToBuffer(underlyingVault2); + + _rebalanceAndValidate(); + + _setData(0, 1e18, 0); + _allocateBalanceToBuffer(underlyingVault1); + _allocateBalanceToBuffer(underlyingVault2); + + _rebalanceAndValidate(); + + _setData(0, 0, 1e18); + _allocateBalanceToBuffer(underlyingVault1); + _allocateBalanceToBuffer(underlyingVault2); + + _rebalanceAndValidate(); + + _setData(1e18, 0, 0); + _allocateBalanceToBuffer(underlyingVault1); + _allocateBalanceToBuffer(underlyingVault2); + + _rebalanceAndValidate(); } function test_SetDataFailsForMismatchedArrayLengths() public { @@ -144,7 +262,7 @@ contract BaseKeeperTest is Test, MainnetActors { vaults[1] = address(2); vm.expectRevert("Array lengths must match"); - baseKeeper.setData(finalRatios, vaults); + keeper.setData(finalRatios, vaults); } function test_SetDataFailsForInvalidRatios() public { @@ -162,28 +280,28 @@ contract BaseKeeperTest is Test, MainnetActors { finalRatios[1] = ratio2; finalRatios[2] = ratio3; - vm.expectRevert("Initial and target ratios must match"); - baseKeeper.setData(finalRatios, vaults); + vm.expectRevert("Target ratios must add up to 100 %"); + keeper.setData(finalRatios, vaults); } function test_CalculateCurrentRatio() public { - setData(0.5e18, 0.25e18, 0.25e18); + _setData(0.5e18, 0.25e18, 0.25e18); - uint256 currentRatio = baseKeeper.calculateCurrentRatio(address(maxVault), maxVault.totalAssets()); + uint256 currentRatio = keeper.calculateCurrentRatio(address(maxVault), maxVault.totalAssets()); assertEq(currentRatio, 1e18); } function test_ShouldRebalance() public { - setData(0.5e18, 0.25e18, 0.25e18); - bool shouldRebalance = baseKeeper.shouldRebalance(); + _setData(0.5e18, 0.25e18, 0.25e18); + bool shouldRebalance = keeper.shouldRebalance(); assertEq(shouldRebalance, true); - setData(1e18, 0, 0); - shouldRebalance = baseKeeper.shouldRebalance(); + _setData(1e18, 0, 0); + shouldRebalance = keeper.shouldRebalance(); assertEq(shouldRebalance, false); } - function setData(uint256 ratio1, uint256 ratio2, uint256 ratio3) public { + function _setData(uint256 ratio1, uint256 ratio2, uint256 ratio3) internal { uint256[] memory finalRatios = new uint256[](3); address[] memory vaults = new address[](3); @@ -195,10 +313,48 @@ contract BaseKeeperTest is Test, MainnetActors { finalRatios[1] = ratio2; finalRatios[2] = ratio3; - baseKeeper.setData(finalRatios, vaults); + keeper.setData(finalRatios, vaults); + + uint256[] memory targetRatios = keeper.finalRatios(); + + assertEq(targetRatios[0], ratio1, "Target ratio 0 incorrect"); + assertEq(targetRatios[1], ratio2, "Target ratio 1 incorrect"); + assertEq(targetRatios[2], ratio3, "Target ratio 2 incorrect"); + } + + function _rebalanceAndValidate() internal { + keeper.rebalance(); + + uint256[] memory initialRatios = keeper.currentRatios(); + uint256[] memory targetRatios = keeper.finalRatios(); + + assertEq(initialRatios.length, 3); + assertEq(targetRatios.length, 3); + + for (uint256 i = 0; i < 3; i++) { + assertEq(initialRatios[i], targetRatios[i], "Expected initial ratios to match target ratios"); + } + } + + function _allocateBalanceToBuffer(IVault vault) internal { + uint256 balance = weth.balanceOf(address(vault)); + _allocateToBuffer(vault, balance); + } + + function _allocateToBuffer(IVault vault, uint256 amount) public { + address[] memory targets = new address[](2); + targets[0] = MC.WETH; + targets[1] = MC.BUFFER; + + uint256[] memory values = new uint256[](2); + values[0] = 0; + values[1] = 0; + + bytes[] memory data = new bytes[](2); + data[0] = abi.encodeWithSignature("approve(address,uint256)", vault.buffer(), amount); + data[1] = abi.encodeWithSignature("deposit(uint256,address)", amount, address(vault)); - assertEq(baseKeeper.targetRatios(0), ratio1, "Target ratio 0 incorrect"); - assertEq(baseKeeper.targetRatios(1), ratio2, "Target ratio 1 incorrect"); - assertEq(baseKeeper.targetRatios(2), ratio3, "Target ratio 2 incorrect"); + vm.prank(PROCESSOR); + vault.processor(targets, values, data); } }