diff --git a/src/LockstakeEngine.sol b/src/LockstakeEngine.sol index 7faf367d..29faf294 100644 --- a/src/LockstakeEngine.sol +++ b/src/LockstakeEngine.sol @@ -89,10 +89,10 @@ contract LockstakeEngine { event File(bytes32 indexed what, address data); event AddFarm(address farm); event DelFarm(address farm); - event Open(address indexed owner, address indexed delegate, address urn); + event Open(address indexed owner, address urn); + event Delegate(address indexed urn, address indexed delegate); event Lock(address indexed urn, uint256 wad); event Free(address indexed urn, uint256 wad, uint256 burn); - event Move(address indexed urn, address indexed delegate); event Draw(address indexed urn, uint256 wad); event Wipe(address indexed urn, uint256 wad); event SelectFarm(address indexed urn, address farm); @@ -187,8 +187,7 @@ contract LockstakeEngine { // --- urn/delegation functions --- - function open(address delegate) external returns (address urn) { - require(delegateFactory.isDelegate(delegate) == 1, "LockstakeEngine/not-valid-delegate"); + function open() external returns (address urn) { uint256 salt = uint256(keccak256(abi.encode(msg.sender, urnsAmt[msg.sender]++))); bytes memory code = abi.encodePacked(type(LockstakeUrn).creationCode, abi.encode(vat, stkGov)); assembly { @@ -196,16 +195,17 @@ contract LockstakeEngine { } require(urn != address(0), "LockstakeEngine/urn-creation-failed"); urnOwners[urn] = msg.sender; - urnDelegates[urn] = delegate; - emit Open(msg.sender, delegate, urn); + emit Open(msg.sender, urn); } function lock(address urn, uint256 wad) external urnOwner(urn) { require(wad <= uint256(type(int256).max), "LockstakeEngine/wad-overflow"); gov.transferFrom(msg.sender, address(this), wad); - address delegate = urnDelegates[urn]; - gov.approve(address(delegate), wad); - DelegateLike(delegate).lock(wad); + address delegate_ = urnDelegates[urn]; + if (delegate_ != address(0)) { + gov.approve(address(delegate_), wad); + DelegateLike(delegate_).lock(wad); + } // TODO: define if we want an internal registry to register how much is locked per user, // the vat.slip and stkGov balance act already as a registry so probably not needed an extra one vat.slip(ilk, urn, int256(wad)); @@ -219,24 +219,32 @@ contract LockstakeEngine { vat.frob(ilk, urn, urn, address(0), -int256(wad), 0); vat.slip(ilk, urn, -int256(wad)); stkGov.burn(urn, wad); - address delegate = urnDelegates[urn]; - DelegateLike(delegate).free(wad); + address delegate_ = urnDelegates[urn]; + if (delegate_ != address(0)) { + DelegateLike(delegate_).free(wad); + } uint256 burn = wad * fee / WAD; gov.burn(address(this), burn); gov.transfer(msg.sender, wad - burn); emit Free(urn, wad, burn); } - function move(address urn, address delegate) external urnOwner(urn) { - require(delegateFactory.isDelegate(delegate) == 1, "LockstakeEngine/not-valid-delegate"); + function delegate(address urn, address delegate_) external urnOwner(urn) { + require(delegate_ == address(0) || delegateFactory.isDelegate(delegate_) == 1, "LockstakeEngine/not-valid-delegate"); address prevDelegate = urnDelegates[urn]; - require(prevDelegate != delegate, "LockstakeEngine/same-delegate"); + require(prevDelegate != delegate_, "LockstakeEngine/same-delegate"); (uint256 wad,) = vat.urns(ilk, urn); - DelegateLike(prevDelegate).free(wad); - gov.approve(address(delegate), wad); - DelegateLike(delegate).lock(wad); - urnDelegates[urn] = delegate; - emit Move(urn, delegate); + if (wad > 0) { + if (prevDelegate != address(0)) { + DelegateLike(prevDelegate).free(wad); + } + if (delegate_ != address(0)) { + gov.approve(address(delegate_), wad); + DelegateLike(delegate_).lock(wad); + } + } + urnDelegates[urn] = delegate_; + emit Delegate(urn, delegate_); } // --- loan functions --- @@ -300,7 +308,10 @@ contract LockstakeEngine { } } stkGov.burn(urn, wad); // Burn the whole liquidated amount of staking token - DelegateLike(urnDelegates[urn]).free(wad); // Undelegate liquidated amount and retain GOV + address delegate_ = urnDelegates[urn]; + if (delegate_ != address(0)) { + DelegateLike(delegate_).free(wad); // Undelegate liquidated amount and retain NGT + } // Urn confiscation happens in Dog contract where ilk vat.gem is sent to the LockstakeClipper emit OnKick(urn, wad); } @@ -320,9 +331,11 @@ contract LockstakeEngine { } gov.burn(address(this), burn); // Burn GOV if (left > 0) { - address delegate = urnDelegates[urn]; - gov.approve(address(delegate), left); - DelegateLike(delegate).lock(left); + address delegate_ = urnDelegates[urn]; + if (delegate_ != address(0)) { + gov.approve(address(delegate_), left); + DelegateLike(delegate_).lock(left); + } vat.slip(ilk, urn, int256(left)); vat.frob(ilk, urn, urn, address(0), int256(left), 0); stkGov.mint(urn, left); diff --git a/test/LockstakeEngine.t.sol b/test/LockstakeEngine.t.sol index 33a41834..c6abf068 100644 --- a/test/LockstakeEngine.t.sol +++ b/test/LockstakeEngine.t.sol @@ -80,10 +80,10 @@ contract AllocatorVaultTest is DssTest { event AddFarm(address farm); event DelFarm(address farm); - event Open(address indexed owner, address indexed delegate, address urn); + event Open(address indexed owner, address urn); event Lock(address indexed urn, uint256 wad); event Free(address indexed urn, uint256 wad, uint256 burn); - event Move(address indexed urn, address indexed delegate); + event Delegate(address indexed urn, address indexed delegate_); event Draw(address indexed urn, uint256 wad); event Wipe(address indexed urn, uint256 wad); event SelectFarm(address indexed urn, address farm); @@ -169,15 +169,16 @@ contract AllocatorVaultTest is DssTest { checkModifier(address(engine), "LockstakeEngine/not-authorized", authedMethods); vm.stopPrank(); - bytes4[] memory urnOwnersMethods = new bytes4[](8); + bytes4[] memory urnOwnersMethods = new bytes4[](9); urnOwnersMethods[0] = engine.lock.selector; urnOwnersMethods[1] = engine.free.selector; - urnOwnersMethods[2] = engine.draw.selector; - urnOwnersMethods[3] = engine.wipe.selector; - urnOwnersMethods[4] = engine.selectFarm.selector; - urnOwnersMethods[5] = engine.stake.selector; - urnOwnersMethods[6] = engine.withdraw.selector; - urnOwnersMethods[7] = engine.getReward.selector; + urnOwnersMethods[2] = engine.delegate.selector; + urnOwnersMethods[3] = engine.draw.selector; + urnOwnersMethods[4] = engine.wipe.selector; + urnOwnersMethods[5] = engine.selectFarm.selector; + urnOwnersMethods[6] = engine.stake.selector; + urnOwnersMethods[7] = engine.withdraw.selector; + urnOwnersMethods[8] = engine.getReward.selector; vm.startPrank(address(0xBEEF)); checkModifier(address(engine), "LockstakeEngine/not-urn-owner", urnOwnersMethods); @@ -204,19 +205,22 @@ contract AllocatorVaultTest is DssTest { assertEq(engine.urnsAmt(address(this)), 0); address urn = engine.getUrn(address(this), 0); vm.expectEmit(true, true, true, true); - emit Open(address(this), voterDelegate, urn); - assertEq(engine.open(voterDelegate), urn); + emit Open(address(this), urn); + assertEq(engine.open(), urn); assertEq(engine.urnsAmt(address(this)), 1); - assertEq(engine.getUrn(address(this), 1), engine.open(voterDelegate)); + assertEq(engine.getUrn(address(this), 1), engine.open()); assertEq(engine.urnsAmt(address(this)), 2); - assertEq(engine.getUrn(address(this), 2), engine.open(voterDelegate)); + assertEq(engine.getUrn(address(this), 2), engine.open()); assertEq(engine.urnsAmt(address(this)), 3); } - function testLockFree() public { + function _testLockFree(bool withDelegate) internal { uint256 initialSupply = gov.totalSupply(); assertEq(gov.balanceOf(address(this)), 100_000 * 10**18); - address urn = engine.open(voterDelegate); + address urn = engine.open(); + if (withDelegate) { + engine.delegate(urn, voterDelegate); + } assertEq(_ink(ilk, urn), 0); assertEq(stkGov.balanceOf(urn), 0); gov.approve(address(engine), 100_000 * 10**18); @@ -226,6 +230,12 @@ contract AllocatorVaultTest is DssTest { assertEq(_ink(ilk, urn), 100_000 * 10**18); assertEq(stkGov.balanceOf(urn), 100_000 * 10**18); assertEq(gov.balanceOf(address(this)), 0); + if (withDelegate) { + assertEq(gov.balanceOf(address(engine)), 0); + assertEq(gov.balanceOf(address(voterDelegate)), 100_000 * 10**18); // Remains in delegate as it is a mock (otherwise it would be in the Chief) + } else { + assertEq(gov.balanceOf(address(engine)), 100_000 * 10**18); + } assertEq(gov.totalSupply(), initialSupply); vm.expectEmit(true, true, true, true); emit Free(urn, 40_000 * 10**18, 40_000 * 10**18 * 15 / 100); @@ -233,11 +243,27 @@ contract AllocatorVaultTest is DssTest { assertEq(_ink(ilk, urn), 60_000 * 10**18); assertEq(stkGov.balanceOf(urn), 60_000 * 10**18); assertEq(gov.balanceOf(address(this)), 40_000 * 10**18 - 40_000 * 10**18 * 15 / 100); + if (withDelegate) { + assertEq(gov.balanceOf(address(engine)), 0); + assertEq(gov.balanceOf(address(voterDelegate)), 60_000 * 10**18); + } else { + assertEq(gov.balanceOf(address(engine)), 60_000 * 10**18); + } assertEq(gov.totalSupply(), initialSupply - 40_000 * 10**18 * 15 / 100); } - function testMove() public { - address urn = engine.open(voterDelegate); + function testLockFreeNoDelegate() public { + _testLockFree(false); + } + + function testLockFreeWithDelegate() public { + _testLockFree(true); + } + + function testDelegate() public { + address urn = engine.open(); + engine.delegate(urn, voterDelegate); + assertEq(engine.urnDelegates(urn), voterDelegate); vm.prank(address(888)); address voterDelegate2 = delFactory.create(); gov.approve(address(engine), 100_000 * 10**18); engine.lock(urn, 100_000 * 10**18); @@ -245,16 +271,26 @@ contract AllocatorVaultTest is DssTest { assertEq(DelegateMock(voterDelegate2).stake(address(engine)), 0); assertEq(gov.balanceOf(voterDelegate), 100_000 * 10**18); assertEq(gov.balanceOf(voterDelegate2), 0); - engine.move(urn, voterDelegate2); + assertEq(gov.balanceOf(address(engine)), 0); + engine.delegate(urn, voterDelegate2); + assertEq(engine.urnDelegates(urn), voterDelegate2); assertEq(DelegateMock(voterDelegate).stake(address(engine)), 0); assertEq(DelegateMock(voterDelegate2).stake(address(engine)), 100_000 * 10**18); assertEq(gov.balanceOf(voterDelegate), 0); assertEq(gov.balanceOf(voterDelegate2), 100_000 * 10**18); + assertEq(gov.balanceOf(address(engine)), 0); + engine.delegate(urn, address(0)); + assertEq(engine.urnDelegates(urn), address(0)); + assertEq(DelegateMock(voterDelegate).stake(address(engine)), 0); + assertEq(DelegateMock(voterDelegate2).stake(address(engine)), 0); + assertEq(gov.balanceOf(voterDelegate), 0); + assertEq(gov.balanceOf(voterDelegate2), 0); + assertEq(gov.balanceOf(address(engine)), 100_000 * 10**18); } function testDrawWipe() public { deal(address(gov), address(this), 100_000 * 10**18, true); - address urn = engine.open(voterDelegate); + address urn = engine.open(); gov.approve(address(engine), 100_000 * 10**18); engine.lock(urn, 100_000 * 10**18); assertEq(_art(ilk, urn), 0); @@ -292,7 +328,7 @@ contract AllocatorVaultTest is DssTest { function testSelectFarm() public { StakingRewardsMock farm2 = new StakingRewardsMock(address(rTok), address(stkGov)); - address urn = engine.open(voterDelegate); + address urn = engine.open(); assertEq(engine.selectedFarm(urn), address(0)); vm.expectRevert("LockstakeEngine/non-existing-farm"); engine.selectFarm(urn, address(farm)); @@ -315,7 +351,7 @@ contract AllocatorVaultTest is DssTest { function testStakeWithdraw() public { vm.prank(pauseProxy); engine.addFarm(address(farm)); - address urn = engine.open(voterDelegate); + address urn = engine.open(); gov.approve(address(engine), 100_000 * 10**18); engine.lock(urn, 100_000 * 10**18); vm.expectRevert("LockstakeEngine/missing-selected-farm"); @@ -342,7 +378,7 @@ contract AllocatorVaultTest is DssTest { function testGetReward() public { vm.prank(pauseProxy); engine.addFarm(address(farm)); - address urn = engine.open(voterDelegate); + address urn = engine.open(); farm.setReward(address(urn), 20_000); assertEq(GemMock(address(farm.rewardsToken())).balanceOf(address(this)), 0); vm.expectEmit(true, true, true, true); @@ -351,7 +387,7 @@ contract AllocatorVaultTest is DssTest { assertEq(GemMock(address(farm.rewardsToken())).balanceOf(address(this)), 20_000); } - function _clipperSetUp() internal returns (address urn) { + function _clipperSetUp(bool withDelegate) internal returns (address urn) { vm.startPrank(pauseProxy); engine.addFarm(address(farm)); clip = new LockstakeClipper(vat, spot, dog, address(engine)); @@ -373,7 +409,10 @@ contract AllocatorVaultTest is DssTest { DogLike(dog).file(ilk, "hole", 10_000 * 10**45); vm.stopPrank(); - urn = engine.open(voterDelegate); + urn = engine.open(); + if (withDelegate) { + engine.delegate(urn, voterDelegate); + } gov.approve(address(engine), 100_000 * 10**18); engine.lock(urn, 100_000 * 10**18); engine.draw(urn, 2_000 * 10**18); @@ -389,11 +428,15 @@ contract AllocatorVaultTest is DssTest { assertEq(clip.kicks(), 1); } - function testOnKickFullNoStaked() public { - address urn = _clipperSetUp(); + function _testOnKickFullNoStaked(bool withDelegate) internal { + address urn = _clipperSetUp(withDelegate); - assertEq(gov.balanceOf(address(voterDelegate)), 100_000 * 10**18); - assertEq(gov.balanceOf(address(engine)), 0); + if (withDelegate) { + assertEq(gov.balanceOf(address(voterDelegate)), 100_000 * 10**18); + assertEq(gov.balanceOf(address(engine)), 0); + } else { + assertEq(gov.balanceOf(address(engine)), 100_000 * 10**18); + } assertEq(stkGov.balanceOf(address(urn)), 100_000 * 10**18); uint256 stkGovInitialSupply = stkGov.totalSupply(); @@ -413,14 +456,24 @@ contract AllocatorVaultTest is DssTest { assertEq(_art(ilk, urn), 0); assertEq(VatLike(vat).gem(ilk, address(clip)), 100_000 * 10**18); - assertEq(gov.balanceOf(address(voterDelegate)), 0); + if (withDelegate) { + assertEq(gov.balanceOf(address(voterDelegate)), 0); + } assertEq(gov.balanceOf(address(engine)), 100_000 * 10**18); assertEq(stkGov.balanceOf(address(urn)), 0); assertEq(stkGov.totalSupply(), stkGovInitialSupply - 100_000 * 10**18); } - function testOnKickPartialNoStaked() public { - address urn = _clipperSetUp(); + function testOnKickFullNoStakedNoDelegate() public { + _testOnKickFullNoStaked(false); + } + + function testOnKickFullNoStakedWithDelegate() public { + _testOnKickFullNoStaked(true); + } + + function _testOnKickPartialNoStaked(bool withDelegate) internal { + address urn = _clipperSetUp(withDelegate); vm.prank(pauseProxy); DogLike(dog).file(ilk, "hole", 500 * 10**45); @@ -442,22 +495,38 @@ contract AllocatorVaultTest is DssTest { assertEq(_art(ilk, urn), 1_500 * 10**18); assertEq(VatLike(vat).gem(ilk, address(clip)), 25_000 * 10**18); - assertEq(gov.balanceOf(address(voterDelegate)), 75_000 * 10**18); - assertEq(gov.balanceOf(address(engine)), 25_000 * 10**18); + if (withDelegate) { + assertEq(gov.balanceOf(address(voterDelegate)), 75_000 * 10**18); + assertEq(gov.balanceOf(address(engine)), 25_000 * 10**18); + } else { + assertEq(gov.balanceOf(address(engine)), 100_000 * 10**18); + } assertEq(stkGov.balanceOf(address(urn)), 75_000 * 10**18); assertEq(stkGov.totalSupply(), stkGovInitialSupply - 25_000 * 10**18); } - function testOnKickFullStakedPartial() public { - address urn = _clipperSetUp(); + function testOnKickPartialNoStakedNoDelegate() public { + _testOnKickPartialNoStaked(false); + } + + function testOnKickPartialNoStakedWithDelegate() public { + _testOnKickPartialNoStaked(true); + } + + function _testOnKickFullStakedPartial(bool withDelegate) private { + address urn = _clipperSetUp(withDelegate); engine.selectFarm(urn, address(farm)); engine.stake(urn, 60_000 * 10**18, 1); assertEq(stkGov.balanceOf(address(urn)), 40_000 * 10**18); assertEq(stkGov.balanceOf(address(farm)), 60_000 * 10**18); - assertEq(gov.balanceOf(address(voterDelegate)), 100_000 * 10**18); - assertEq(gov.balanceOf(address(engine)), 0); + if (withDelegate) { + assertEq(gov.balanceOf(address(voterDelegate)), 100_000 * 10**18); + assertEq(gov.balanceOf(address(engine)), 0); + } else { + assertEq(gov.balanceOf(address(engine)), 100_000 * 10**18); + } assertEq(stkGov.balanceOf(address(urn)), 40_000 * 10**18); assertEq(stkGov.balanceOf(address(farm)), 60_000 * 10**18); uint256 stkGovInitialSupply = stkGov.totalSupply(); @@ -478,15 +547,25 @@ contract AllocatorVaultTest is DssTest { assertEq(_art(ilk, urn), 0); assertEq(VatLike(vat).gem(ilk, address(clip)), 100_000 * 10**18); - assertEq(gov.balanceOf(address(voterDelegate)), 0); + if (withDelegate) { + assertEq(gov.balanceOf(address(voterDelegate)), 0); + } assertEq(gov.balanceOf(address(engine)), 100_000 * 10**18); assertEq(stkGov.balanceOf(address(urn)), 0); assertEq(stkGov.balanceOf(address(farm)), 0); assertEq(stkGov.totalSupply(), stkGovInitialSupply - 100_000 * 10**18); } - function testOnKickPartialStakedPartialNoWithdraw() public { - address urn = _clipperSetUp(); + function testOnKickFullStakedPartialNoDelegate() public { + _testOnKickFullStakedPartial(false); + } + + function testOnKickFullStakedPartialWithDelegate() public { + _testOnKickFullStakedPartial(true); + } + + function _testOnKickPartialStakedPartialNoWithdraw(bool withDelegate) internal { + address urn = _clipperSetUp(withDelegate); engine.selectFarm(urn, address(farm)); engine.stake(urn, 60_000 * 10**18, 1); @@ -513,15 +592,27 @@ contract AllocatorVaultTest is DssTest { assertEq(_art(ilk, urn), 1_500 * 10**18); assertEq(VatLike(vat).gem(ilk, address(clip)), 25_000 * 10**18); - assertEq(gov.balanceOf(address(voterDelegate)), 75_000 * 10**18); - assertEq(gov.balanceOf(address(engine)), 25_000 * 10**18); + if (withDelegate) { + assertEq(gov.balanceOf(address(voterDelegate)), 75_000 * 10**18); + assertEq(gov.balanceOf(address(engine)), 25_000 * 10**18); + } else { + assertEq(gov.balanceOf(address(engine)), 100_000 * 10**18); + } assertEq(stkGov.balanceOf(address(urn)), 15_000 * 10**18); assertEq(stkGov.balanceOf(address(farm)), 60_000 * 10**18); assertEq(stkGov.totalSupply(), stkGovInitialSupply - 25_000 * 10**18); } - function testOnKickPartialStakedPartialWithdraw() public { - address urn = _clipperSetUp(); + function testOnKickPartialStakedPartialNoWithdrawNoDelegate() public { + _testOnKickPartialStakedPartialNoWithdraw(false); + } + + function testOnKickPartialStakedPartialNoWithdrawWithDelegate() public { + _testOnKickPartialStakedPartialNoWithdraw(true); + } + + function _testOnKickPartialStakedPartialWithdraw(bool withDelegate) public { + address urn = _clipperSetUp(withDelegate); engine.selectFarm(urn, address(farm)); engine.stake(urn, 80_000 * 10**18, 1); @@ -548,18 +639,34 @@ contract AllocatorVaultTest is DssTest { assertEq(_art(ilk, urn), 1_500 * 10**18); assertEq(VatLike(vat).gem(ilk, address(clip)), 25_000 * 10**18); - assertEq(gov.balanceOf(address(voterDelegate)), 75_000 * 10**18); - assertEq(gov.balanceOf(address(engine)), 25_000 * 10**18); + if (withDelegate) { + assertEq(gov.balanceOf(address(voterDelegate)), 75_000 * 10**18); + assertEq(gov.balanceOf(address(engine)), 25_000 * 10**18); + } else { + assertEq(gov.balanceOf(address(engine)), 100_000 * 10**18); + } assertEq(stkGov.balanceOf(address(urn)), 0); assertEq(stkGov.balanceOf(address(farm)), 75_000 * 10**18); assertEq(stkGov.totalSupply(), stkGovInitialSupply - 25_000 * 10**18); } - function testOnTake() public { - address urn = _clipperSetUp(); + function testOnKickPartialStakedPartialWithdrawNoDelegate() public { + _testOnKickPartialStakedPartialWithdraw(false); + } - assertEq(gov.balanceOf(address(voterDelegate)), 100_000 * 10**18); - assertEq(gov.balanceOf(address(engine)), 0); + function testOnKickPartialStakedPartialWithdrawWithDelegate() public { + _testOnKickPartialStakedPartialWithdraw(true); + } + + function _testOnTake(bool withDelegate) internal { + address urn = _clipperSetUp(withDelegate); + + if (withDelegate) { + assertEq(gov.balanceOf(address(voterDelegate)), 100_000 * 10**18); + assertEq(gov.balanceOf(address(engine)), 0); + } else { + assertEq(gov.balanceOf(address(engine)), 100_000 * 10**18); + } assertEq(stkGov.balanceOf(address(urn)), 100_000 * 10**18); uint256 govInitialSupply = gov.totalSupply(); @@ -583,6 +690,9 @@ contract AllocatorVaultTest is DssTest { assertEq(_art(ilk, urn), 0); assertEq(VatLike(vat).gem(ilk, address(clip)), 100_000 * 10**18); + if (withDelegate) { + assertEq(gov.balanceOf(address(voterDelegate)), 0); + } assertEq(gov.balanceOf(address(voterDelegate)), 0); assertEq(gov.balanceOf(address(engine)), 100_000 * 10**18); assertEq(stkGov.balanceOf(address(urn)), 0); @@ -608,7 +718,9 @@ contract AllocatorVaultTest is DssTest { assertEq(_art(ilk, urn), 0); assertEq(VatLike(vat).gem(ilk, address(clip)), 80_000 * 10**18); - assertEq(gov.balanceOf(address(voterDelegate)), 0); + if (withDelegate) { + assertEq(gov.balanceOf(address(voterDelegate)), 0); + } assertEq(gov.balanceOf(address(engine)), 80_000 * 10**18); assertEq(stkGov.balanceOf(address(urn)), 0); assertEq(stkGov.totalSupply(), stkGovInitialSupply - 100_000 * 10**18); @@ -629,19 +741,35 @@ contract AllocatorVaultTest is DssTest { assertEq(_art(ilk, urn), 0); assertEq(VatLike(vat).gem(ilk, address(clip)), 0); - assertEq(gov.balanceOf(address(voterDelegate)), (100_000 - 32_000 * 1.15) * 10**18); - assertEq(gov.balanceOf(address(engine)), 0); + if (withDelegate) { + assertEq(gov.balanceOf(address(voterDelegate)), (100_000 - 32_000 * 1.15) * 10**18); + assertEq(gov.balanceOf(address(engine)), 0); + } else { + assertEq(gov.balanceOf(address(engine)), (100_000 - 32_000 * 1.15) * 10**18); + } assertEq(gov.totalSupply(), govInitialSupply - 32_000 * 0.15 * 10**18); assertEq(stkGov.balanceOf(address(urn)), (100_000 - 32_000 * 1.15) * 10**18); assertEq(stkGov.totalSupply(), stkGovInitialSupply - 32_000 * 1.15 * 10**18); assertEq(VatLike(vat).dai(vow), vowInitialBalance + 2_000 * 10**45); } - function testOnTakePartialBurn() public { - address urn = _clipperSetUp(); + function testOnTakeNoDelegate() public { + _testOnTake(false); + } - assertEq(gov.balanceOf(address(voterDelegate)), 100_000 * 10**18); - assertEq(gov.balanceOf(address(engine)), 0); + function testOnTakeWithDelegate() public { + _testOnTake(true); + } + + function _testOnTakePartialBurn(bool withDelegate) internal { + address urn = _clipperSetUp(withDelegate); + + if (withDelegate) { + assertEq(gov.balanceOf(address(voterDelegate)), 100_000 * 10**18); + assertEq(gov.balanceOf(address(engine)), 0); + } else { + assertEq(gov.balanceOf(address(engine)), 100_000 * 10**18); + } assertEq(stkGov.balanceOf(address(urn)), 100_000 * 10**18); uint256 govInitialSupply = gov.totalSupply(); @@ -665,7 +793,9 @@ contract AllocatorVaultTest is DssTest { assertEq(_art(ilk, urn), 0); assertEq(VatLike(vat).gem(ilk, address(clip)), 100_000 * 10**18); - assertEq(gov.balanceOf(address(voterDelegate)), 0); + if (withDelegate) { + assertEq(gov.balanceOf(address(voterDelegate)), 0); + } assertEq(gov.balanceOf(address(engine)), 100_000 * 10**18); assertEq(stkGov.balanceOf(address(urn)), 0); assertEq(stkGov.totalSupply(), stkGovInitialSupply - 100_000 * 10**18); @@ -683,7 +813,9 @@ contract AllocatorVaultTest is DssTest { assertEq(_art(ilk, urn), 0); assertEq(VatLike(vat).gem(ilk, address(clip)), 0); - assertEq(gov.balanceOf(address(voterDelegate)), 0); + if (withDelegate) { + assertEq(gov.balanceOf(address(voterDelegate)), 0); + } assertEq(gov.balanceOf(address(engine)), 0); assertEq(gov.totalSupply(), govInitialSupply - (100_000 * 10**18 - 91428571428571428571428)); // Can't burn 15% of 91428571428571428571428 assertEq(stkGov.balanceOf(address(urn)), 0); @@ -691,11 +823,23 @@ contract AllocatorVaultTest is DssTest { assertEq(VatLike(vat).dai(vow), vowInitialBalance + 2_000 * 10**45); } - function testOnTakeNoBurn() public { - address urn = _clipperSetUp(); + function testOnTakePartialBurnNoDelegate() public { + _testOnTakePartialBurn(false); + } - assertEq(gov.balanceOf(address(voterDelegate)), 100_000 * 10**18); - assertEq(gov.balanceOf(address(engine)), 0); + function testOnTakePartialBurnWithDelegate() public { + _testOnTakePartialBurn(true); + } + + function _testOnTakeNoBurn(bool withDelegate) internal { + address urn = _clipperSetUp(withDelegate); + + if (withDelegate) { + assertEq(gov.balanceOf(address(voterDelegate)), 100_000 * 10**18); + assertEq(gov.balanceOf(address(engine)), 0); + } else { + assertEq(gov.balanceOf(address(engine)), 100_000 * 10**18); + } assertEq(stkGov.balanceOf(address(urn)), 100_000 * 10**18); uint256 govInitialSupply = gov.totalSupply(); @@ -719,7 +863,9 @@ contract AllocatorVaultTest is DssTest { assertEq(_art(ilk, urn), 0); assertEq(VatLike(vat).gem(ilk, address(clip)), 100_000 * 10**18); - assertEq(gov.balanceOf(address(voterDelegate)), 0); + if (withDelegate) { + assertEq(gov.balanceOf(address(voterDelegate)), 0); + } assertEq(gov.balanceOf(address(engine)), 100_000 * 10**18); assertEq(stkGov.balanceOf(address(urn)), 0); assertEq(stkGov.totalSupply(), stkGovInitialSupply - 100_000 * 10**18); @@ -737,7 +883,9 @@ contract AllocatorVaultTest is DssTest { assertEq(_art(ilk, urn), 0); assertEq(VatLike(vat).gem(ilk, address(clip)), 0); - assertEq(gov.balanceOf(address(voterDelegate)), 0); + if (withDelegate) { + assertEq(gov.balanceOf(address(voterDelegate)), 0); + } assertEq(gov.balanceOf(address(engine)), 0); assertEq(gov.totalSupply(), govInitialSupply); // Can't burn anything assertEq(stkGov.balanceOf(address(urn)), 0); @@ -745,11 +893,23 @@ contract AllocatorVaultTest is DssTest { assertLt(VatLike(vat).dai(vow), vowInitialBalance + 2_000 * 10**45); // Doesn't recover full debt } - function testOnYank() public { - address urn = _clipperSetUp(); + function testOnTakeNoBurnNoDelegate() public { + _testOnTakeNoBurn(false); + } - assertEq(gov.balanceOf(address(voterDelegate)), 100_000 * 10**18); - assertEq(gov.balanceOf(address(engine)), 0); + function testOnTakeNoBurnWithDelegate() public { + _testOnTakeNoBurn(true); + } + + function _testOnYank(bool withDelegate) internal { + address urn = _clipperSetUp(withDelegate); + + if (withDelegate) { + assertEq(gov.balanceOf(address(voterDelegate)), 100_000 * 10**18); + assertEq(gov.balanceOf(address(engine)), 0); + } else { + assertEq(gov.balanceOf(address(engine)), 100_000 * 10**18); + } assertEq(stkGov.balanceOf(address(urn)), 100_000 * 10**18); uint256 govInitialSupply = gov.totalSupply(); @@ -779,4 +939,12 @@ contract AllocatorVaultTest is DssTest { assertEq(gov.totalSupply(), govInitialSupply - 100_000 * 10**18); } + + function testOnYankNoDelegate() public { + _testOnYank(false); + } + + function testOnYankWithDelegate() public { + _testOnYank(true); + } }