Skip to content

Commit

Permalink
Fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexSaplin committed Aug 21, 2023
1 parent 89d3633 commit f635da8
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 41 deletions.
3 changes: 1 addition & 2 deletions script/scripts/CompoundingMigration.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,12 @@ contract BOBPoolMigration is Script, StdCheats {
ZkBobCompoundingMixin.YieldParams({
yield: address(yieldProxy),
maxInvestedAmount: 50_000 ether / D,
buffer: 400 ether / D,
buffer: uint96(400 ether / D),
dust: uint96(0.5 ether / D),
interestReceiver: address(this),
yieldOperator: address(this)
})
);

}

function makeWithdrawal() internal {
Expand Down
69 changes: 55 additions & 14 deletions src/zkbob/ZkBobCompoundingMixin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ abstract contract ZkBobCompoundingMixin is ZkBobPool {
// maximum amount of underlying tokens that can be invested into vault
uint256 maxInvestedAmount;
// expected amount of underlying tokens to be left at the pool after successful rebalance
uint256 buffer;
uint96 buffer;
// slippage/rounding protection buffer, small part of accumulated interest that is non-claimable
uint96 dust;
// address to receive accumulated interest during the rebalance
Expand All @@ -30,8 +30,8 @@ abstract contract ZkBobCompoundingMixin is ZkBobPool {
YieldParams public yieldParams;

event UpdateYieldParams(YieldParams yieldParams);
event Claimed(address indexed token, address indexed yield, uint256 amount);
event Rebalance(uint256 toPool, uint256 toYield);
event Claimed(address indexed yield, uint256 amount);
event Rebalance(address indexed yield, uint256 withdrawn, uint256 deposited);

// @inheritdoc ZkBobPool
function _withdrawToken(address _user, uint256 _tokenAmount) internal override {
Expand All @@ -45,9 +45,11 @@ abstract contract ZkBobCompoundingMixin is ZkBobPool {
if (vaultAmount >= remainder + buffer) {
investedAssetsAmount = vaultAmount - remainder - buffer;
yieldVault.withdraw(remainder + buffer, address(this), address(this));
emit Rebalance(address(yieldVault), remainder + buffer, 0);
} else {
investedAssetsAmount = vaultAmount - remainder;
yieldVault.withdraw(remainder, address(this), address(this));
investedAssetsAmount = 0;
yieldVault.withdraw(vaultAmount, address(this), address(this));
emit Rebalance(address(yieldVault), vaultAmount, 0);
}
}
IERC20(token).safeTransfer(_user, _tokenAmount);
Expand Down Expand Up @@ -75,6 +77,7 @@ abstract contract ZkBobCompoundingMixin is ZkBobPool {
}
if (yieldAddress != address(0)) {
IERC20(token).approve(yieldAddress, 0);
_claim(0, yieldAddress, yieldParams.interestReceiver);
}
}

Expand All @@ -96,6 +99,14 @@ abstract contract ZkBobCompoundingMixin is ZkBobPool {
YieldParams storage params = yieldParams;
IERC4626 yieldVault = IERC4626(params.yield);

uint256 currentDust = params.dust;
if (currentDust > maxRebalanceAmount) {
return;
}
if (minRebalanceAmount < currentDust) {
minRebalanceAmount = currentDust;
}

if (address(yieldVault) == address(0)) {
return;
}
Expand All @@ -116,14 +127,32 @@ abstract contract ZkBobCompoundingMixin is ZkBobPool {
if (balancesDiff <= params.dust) {
return;
}
uint256 vaultAssets = investedAssetsAmount;
uint256 maxInvestedAmount = params.maxInvestedAmount;
if (underlyingBalance > buffer) {
investedAssetsAmount += balancesDiff;
if (vaultAssets + balancesDiff > maxInvestedAmount) {
balancesDiff = maxInvestedAmount - vaultAssets;
if (balancesDiff < minRebalanceAmount) {
return;
}
investedAssetsAmount = maxInvestedAmount;
} else {
investedAssetsAmount += balancesDiff;
}
yieldVault.deposit(balancesDiff, address(this));
emit Rebalance(0, balancesDiff);
emit Rebalance(address(yieldVault), 0, balancesDiff);
} else {
investedAssetsAmount -= balancesDiff;
if (balancesDiff > vaultAssets) {
balancesDiff = vaultAssets;
if (balancesDiff < minRebalanceAmount) {
return;
}
investedAssetsAmount = 0;
} else {
investedAssetsAmount -= balancesDiff;
}
yieldVault.withdraw(balancesDiff, address(this), address(this));
emit Rebalance(balancesDiff, 0);
emit Rebalance(address(yieldVault), balancesDiff, 0);
}
}

Expand All @@ -136,9 +165,9 @@ abstract contract ZkBobCompoundingMixin is ZkBobPool {
function claim(uint256 minClaimAmount) external returns (uint256) {
YieldParams storage params = yieldParams;

IERC4626 yieldVault = IERC4626(params.yield);
address yieldAddress = params.yield;

if (address(yieldVault) == address(0)) {
if (yieldAddress == address(0)) {
return 0;
}

Expand All @@ -150,17 +179,29 @@ abstract contract ZkBobCompoundingMixin is ZkBobPool {
uint256 dust = params.dust;
minClaimAmount = minClaimAmount > dust ? minClaimAmount : dust;

return _claim(minClaimAmount, yieldAddress, params.interestReceiver);
}

function _claim(
uint256 minClaimAmount,
address yieldAddress,
address interestReceiver
)
internal
returns (uint256)
{
IERC4626 yieldVault = IERC4626(yieldAddress);
uint256 currentInvestedSharesAmount = yieldVault.balanceOf(address(this));

uint256 toClaimAmount = yieldVault.convertToAssets(currentInvestedSharesAmount) - investedAssetsAmount;

if (toClaimAmount < minClaimAmount) {
if (toClaimAmount < minClaimAmount || toClaimAmount == 0) {
return 0;
}

yieldVault.withdraw(toClaimAmount, address(this), address(this));
yieldVault.withdraw(toClaimAmount, interestReceiver, address(this));

emit Claimed(token, address(yieldVault), toClaimAmount);
emit Claimed(address(yieldVault), toClaimAmount);
return toClaimAmount;
}
}
2 changes: 1 addition & 1 deletion test/interfaces/IZkBobPoolAdmin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ interface IZkBobPoolAdmin {
// maximum amount of underlying tokens that can be invested into vault
uint256 maxInvestedAmount;
// expected amount of underlying tokens to be left at the pool after successful rebalance
uint256 buffer;
uint96 buffer;
// slippage/rounding protection buffer, small part of accumulated interest that is non-claimable
uint96 dust;
// address to receive accumulated interest during the rebalance
Expand Down
52 changes: 28 additions & 24 deletions test/zkbob/ZkBobPool.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ abstract contract AbstractZkBobPoolTest is AbstractForkTest {
event RefundDirectDeposit(uint256 indexed nonce, address receiver, uint256 amount);
event CompleteDirectDepositBatch(uint256[] indices);
event UpdateYieldParams(IZkBobPoolAdmin.YieldParams yieldParams);
event Claimed(address indexed token, address indexed yield, uint256 amount);
event Rebalance(uint256 toPool, uint256 toYield);
event Claimed(address indexed yield, uint256 amount);
event Rebalance(address indexed yield, uint256 withdrawn, uint256 deposited);

IZkBobPoolAdmin pool;
IZkBobDirectDepositsAdmin queue;
Expand Down Expand Up @@ -180,7 +180,7 @@ abstract contract AbstractZkBobPoolTest is AbstractForkTest {
IZkBobPoolAdmin.YieldParams({
yield: address(yieldProxy),
maxInvestedAmount: 50_000 ether / D,
buffer: 5_000 ether / D,
buffer: uint96(5_000 ether / D),
dust: uint96(0.5 ether / D),
interestReceiver: address(this),
yieldOperator: address(this)
Expand Down Expand Up @@ -657,6 +657,8 @@ abstract contract AbstractZkBobPoolTest is AbstractForkTest {
return;
}

address yieldAddress = pool.yieldParams().yield;

deal(token, user1, 11_000 ether / D);
bytes memory data1 = _encodePermitDeposit(int256(10_000 ether / D), 0);
_transact(data1);
Expand All @@ -667,8 +669,8 @@ abstract contract AbstractZkBobPoolTest is AbstractForkTest {
// POOL -> YIELD

// When diff is more than max bound
vm.expectEmit(false, false, false, true);
emit Rebalance(0, 4_000 ether / D);
vm.expectEmit(true, false, false, true);
emit Rebalance(yieldAddress, 0, 4_000 ether / D);
pool.rebalance(3_000 ether / D, 4_000 ether / D);
poolBalance = IERC20(token).balanceOf(address(pool));
assertEq(poolBalance, 6_000 ether / D);
Expand All @@ -679,8 +681,8 @@ abstract contract AbstractZkBobPoolTest is AbstractForkTest {
assertEq(poolBalance, 6_000 ether / D);

// When bounds set in wrong order
vm.expectEmit(false, false, false, true);
emit Rebalance(0, (1_000 ether - 0.4 ether) / D);
vm.expectEmit(true, false, false, true);
emit Rebalance(yieldAddress, 0, (1_000 ether - 0.4 ether) / D);
pool.rebalance((1_000 ether - 0.4 ether) / D, 0);
poolBalance = IERC20(token).balanceOf(address(pool));
assertEq(poolBalance, 5_000.4 ether / D);
Expand All @@ -694,15 +696,15 @@ abstract contract AbstractZkBobPoolTest is AbstractForkTest {
IZkBobPoolAdmin.YieldParams({
yield: pool.yieldParams().yield,
maxInvestedAmount: 50_000 ether / D,
buffer: 4_000 ether / D,
buffer: uint96(4_000 ether / D),
dust: uint96(0.5 ether / D),
interestReceiver: address(this),
yieldOperator: address(this)
})
);

vm.expectEmit(false, false, false, true);
emit Rebalance(0, 1000.4 ether / D);
vm.expectEmit(true, false, false, true);
emit Rebalance(yieldAddress, 0, 1000.4 ether / D);
pool.rebalance(400 ether / D, 2000 ether / D);
poolBalance = IERC20(token).balanceOf(address(pool));
assertEq(poolBalance, 4_000 ether / D);
Expand All @@ -711,7 +713,7 @@ abstract contract AbstractZkBobPoolTest is AbstractForkTest {
IZkBobPoolAdmin.YieldParams({
yield: pool.yieldParams().yield,
maxInvestedAmount: 50_000 ether / D,
buffer: 5_000 ether / D,
buffer: uint96(5_000 ether / D),
dust: uint96(0.5 ether / D),
interestReceiver: address(this),
yieldOperator: address(this)
Expand All @@ -721,8 +723,8 @@ abstract contract AbstractZkBobPoolTest is AbstractForkTest {
// YIELD -> POOL

// When diff is more than max bound
vm.expectEmit(false, false, false, true);
emit Rebalance(600 ether / D, 0);
vm.expectEmit(true, false, false, true);
emit Rebalance(yieldAddress, 600 ether / D, 0);
pool.rebalance(400 ether / D, 600 ether / D);
poolBalance = IERC20(token).balanceOf(address(pool));
assertEq(poolBalance, 4_600 ether / D);
Expand All @@ -733,8 +735,8 @@ abstract contract AbstractZkBobPoolTest is AbstractForkTest {
assertEq(poolBalance, 4_600 ether / D);

// buffer - dust < poolBalance < buffer
vm.expectEmit(false, false, false, true);
emit Rebalance(399.5 ether / D, 0);
vm.expectEmit(true, false, false, true);
emit Rebalance(yieldAddress, 399.5 ether / D, 0);
pool.rebalance(399.5 ether / D, 399.5 ether / D);
poolBalance = IERC20(token).balanceOf(address(pool));
assertEq(poolBalance, 4_999.5 ether / D);
Expand All @@ -759,7 +761,7 @@ abstract contract AbstractZkBobPoolTest is AbstractForkTest {
IZkBobPoolAdmin.YieldParams({
yield: address(0),
maxInvestedAmount: 50_000 ether / D,
buffer: 4_000 ether / D,
buffer: uint96(4_000 ether / D),
dust: uint96(0.5 ether / D),
interestReceiver: address(this),
yieldOperator: address(this)
Expand Down Expand Up @@ -787,7 +789,7 @@ abstract contract AbstractZkBobPoolTest is AbstractForkTest {
IZkBobPoolAdmin.YieldParams({
yield: pool.yieldParams().yield,
maxInvestedAmount: 50_000 ether / D,
buffer: 5_000 ether / D,
buffer: uint96(5_000 ether / D),
dust: uint96(0.5 ether / D),
interestReceiver: address(this),
yieldOperator: address(0)
Expand All @@ -803,7 +805,7 @@ abstract contract AbstractZkBobPoolTest is AbstractForkTest {
IZkBobPoolAdmin.YieldParams({
yield: pool.yieldParams().yield,
maxInvestedAmount: 50_000 ether / D,
buffer: 5_000 ether / D,
buffer: uint96(5_000 ether / D),
dust: uint96(0.5 ether / D),
interestReceiver: address(this),
yieldOperator: address(this)
Expand All @@ -825,7 +827,7 @@ abstract contract AbstractZkBobPoolTest is AbstractForkTest {
IZkBobPoolAdmin.YieldParams memory yieldParams = IZkBobPoolAdmin.YieldParams({
yield: yieldAddress,
maxInvestedAmount: 50_000 ether / D,
buffer: 5_000 ether / D,
buffer: uint96(5_000 ether / D),
dust: uint96(0.5 ether / D),
interestReceiver: address(this),
yieldOperator: address(this)
Expand All @@ -852,7 +854,7 @@ abstract contract AbstractZkBobPoolTest is AbstractForkTest {
yieldParams = IZkBobPoolAdmin.YieldParams({
yield: address(0),
maxInvestedAmount: 50_000 ether / D,
buffer: 5_000 ether / D,
buffer: uint96(5_000 ether / D),
dust: uint96(0.5 ether / D),
interestReceiver: address(this),
yieldOperator: address(this)
Expand All @@ -869,7 +871,7 @@ abstract contract AbstractZkBobPoolTest is AbstractForkTest {
yieldParams = IZkBobPoolAdmin.YieldParams({
yield: address(0),
maxInvestedAmount: 50_000 ether / D,
buffer: 5_000 ether / D,
buffer: uint96(5_000 ether / D),
dust: uint96(0.5 ether / D),
interestReceiver: address(this),
yieldOperator: address(this)
Expand All @@ -883,7 +885,7 @@ abstract contract AbstractZkBobPoolTest is AbstractForkTest {
yieldParams = IZkBobPoolAdmin.YieldParams({
yield: yieldAddress,
maxInvestedAmount: 50_000 ether / D,
buffer: 5_000 ether / D,
buffer: uint96(5_000 ether / D),
dust: uint96(0.5 ether / D),
interestReceiver: address(0),
yieldOperator: address(this)
Expand All @@ -907,7 +909,7 @@ abstract contract AbstractZkBobPoolTest is AbstractForkTest {
IZkBobPoolAdmin.YieldParams({
yield: yieldAddress,
maxInvestedAmount: 50_000 ether / D,
buffer: 4_000 ether / D,
buffer: uint96(4_000 ether / D),
dust: 0,
interestReceiver: address(this),
yieldOperator: address(this)
Expand Down Expand Up @@ -957,7 +959,7 @@ abstract contract AbstractZkBobPoolTest is AbstractForkTest {
IZkBobPoolAdmin.YieldParams({
yield: yieldAddress,
maxInvestedAmount: 50_000 ether / D,
buffer: 5_000 ether / D,
buffer: uint96(5_000 ether / D),
dust: uint96(0.5 ether / D),
interestReceiver: user2,
yieldOperator: address(this)
Expand All @@ -969,6 +971,8 @@ abstract contract AbstractZkBobPoolTest is AbstractForkTest {
uint256 claimed = pool.claim(0);
vm.warp(block.timestamp + 365 days);

vm.expectEmit(true, false, false, false);
emit Claimed(yieldAddress, 0);
claimed = pool.claim(0);
assertGt(claimed, 0);
}
Expand Down

0 comments on commit f635da8

Please sign in to comment.