diff --git a/src/DssVest.sol b/src/DssVest.sol index 0bc4e807..99082c9f 100644 --- a/src/DssVest.sol +++ b/src/DssVest.sol @@ -458,6 +458,13 @@ contract DssVestSuckable is DssVest { vat.suck(chainlog.getAddress("MCD_VOW"), address(this), mul(_amt, RAY)); join.exit(_guy, _amt); } + + /** + @dev Compatibility with older implementations of `DssVestSuckable` + */ + function daiJoin() external view returns (address) { + return address(join); + } } /* diff --git a/src/DssVest.t.sol b/src/DssVest.t.sol index 00f30750..a7fde941 100644 --- a/src/DssVest.t.sol +++ b/src/DssVest.t.sol @@ -26,7 +26,7 @@ interface GemLike { function approve(address, uint256) external returns (bool); } -interface DaiLike is GemLike { +interface ERC20Like is GemLike { function balanceOf(address) external returns (uint256); } @@ -92,6 +92,7 @@ contract DssVestTest is DSTest { DssVestMintable mVest; DssVestSuckable sVest; + DssVestSuckable sVestUsds; DssVestTransferrable tVest; Manager boss; @@ -99,8 +100,9 @@ contract DssVestTest is DSTest { DSTokenLike gem; MkrAuthorityLike authority; VatLike vat; - DaiLike dai; + ERC20Like dai; JoinLike daiJoin; + ERC20Like usds; JoinLike usdsJoin; EndLike end; @@ -113,16 +115,19 @@ contract DssVestTest is DSTest { gem = DSTokenLike ( chainlog.getAddress("MCD_GOV")); authority = MkrAuthorityLike( chainlog.getAddress("GOV_GUARD")); vat = VatLike( chainlog.getAddress("MCD_VAT")); - dai = DaiLike( chainlog.getAddress("MCD_DAI")); + dai = ERC20Like( chainlog.getAddress("MCD_DAI")); daiJoin = JoinLike( chainlog.getAddress("MCD_JOIN_DAI")); + usds = ERC20Like( chainlog.getAddress("USDS")); usdsJoin = JoinLike( chainlog.getAddress("USDS_JOIN")); end = EndLike( chainlog.getAddress("MCD_END")); VOW = chainlog.getAddress("MCD_VOW"); mVest = new DssVestMintable(address(gem)); mVest.file("cap", (2000 * WAD) / (4 * 365 days)); - sVest = new DssVestSuckable(address(chainlog), address(usdsJoin)); + sVest = new DssVestSuckable(address(chainlog), address(daiJoin)); sVest.file("cap", (2000 * WAD) / (4 * 365 days)); + sVestUsds = new DssVestSuckable(address(chainlog), address(usdsJoin)); + sVestUsds.file("cap", (2000 * WAD) / (4 * 365 days)); boss = new Manager(); tVest = new DssVestTransferrable(address(boss), address(dai)); tVest.file("cap", (2000 * WAD) / (4 * 365 days)); @@ -145,6 +150,13 @@ contract DssVestTest is DSTest { ); assertEq(vat.wards(address(sVest)), 1); + hevm.store( + address(vat), + keccak256(abi.encode(address(sVestUsds), uint256(0))), + bytes32(uint256(1)) + ); + assertEq(vat.wards(address(sVestUsds)), 1); + // Give boss 10000 DAI hevm.store( address(dai), @@ -152,6 +164,14 @@ contract DssVestTest is DSTest { bytes32(uint256(10000 * WAD)) ); assertEq(dai.balanceOf(address(boss)), 10000 * WAD); + + // Give boss 10000 USDS + hevm.store( + address(usds), + keccak256(abi.encode(address(boss), uint(2))), + bytes32(uint256(10000 * WAD)) + ); + assertEq(usds.balanceOf(address(boss)), 10000 * WAD, "Failed to check usds balance"); } function testCost() public { @@ -290,34 +310,34 @@ contract DssVestTest is DSTest { function testAccrued() public { uint256 id = mVest.create(address(this), 100 * days_vest, block.timestamp + 10 days, 100 days, 0, address(0)); - assertTrue(mVest.valid(id)); + assertTrue(mVest.valid(id), "0"); - assertEq(mVest.accrued(id), 0); + assertEq(mVest.accrued(id), 0, "1"); hevm.warp(block.timestamp + 43200); - assertEq(mVest.unpaid(id), 0); // inside cliff - assertEq(mVest.accrued(id), 0); + assertEq(mVest.unpaid(id), 0, "2"); // inside cliff + assertEq(mVest.accrued(id), 0, "3"); hevm.warp(block.timestamp + 12 hours + 11 days); - assertEq(mVest.unpaid(id), days_vest * 2); // past cliff - assertEq(mVest.accrued(id), days_vest * 2); + assertEq(mVest.unpaid(id), days_vest * 2, "3.1"); // past cliff + assertEq(mVest.accrued(id), days_vest * 2, "4"); hevm.warp(block.timestamp + 2 days); - assertEq(mVest.unpaid(id), days_vest * 4); // past cliff - assertEq(mVest.accrued(id), days_vest * 4); + assertEq(mVest.unpaid(id), days_vest * 4, "5"); // past cliff + assertEq(mVest.accrued(id), days_vest * 4, "6"); mVest.vest(id); - assertEq(mVest.unpaid(id), 0); - assertEq(mVest.accrued(id), days_vest * 4); - assertEq(gem.balanceOf(address(this)), days_vest * 4); + assertEq(mVest.unpaid(id), 0, "7"); + assertEq(mVest.accrued(id), days_vest * 4, "8"); + assertEq(gem.balanceOf(address(this)), days_vest * 4, "9"); hevm.warp(block.timestamp + 10 days); - assertEq(mVest.unpaid(id), days_vest * 10); - assertEq(mVest.accrued(id), days_vest * 14); + assertEq(mVest.unpaid(id), days_vest * 10, "10"); + assertEq(mVest.accrued(id), days_vest * 14, "11"); mVest.vest(id); - assertEq(mVest.unpaid(id), 0); - assertEq(mVest.accrued(id), days_vest * 14); - assertEq(gem.balanceOf(address(this)), days_vest * 14); + assertEq(mVest.unpaid(id), 0, "12"); + assertEq(mVest.accrued(id), days_vest * 14, "13"); + assertEq(gem.balanceOf(address(this)), days_vest * 14, "14"); hevm.warp(block.timestamp + 120 days); // vesting complete - assertEq(mVest.unpaid(id), days_vest * 86); - assertEq(mVest.accrued(id), days_vest * 100); + assertEq(mVest.unpaid(id), days_vest * 86, "15"); + assertEq(mVest.accrued(id), days_vest * 100, "16"); mVest.vest(id); - assertEq(gem.balanceOf(address(this)), 100 * days_vest); + assertEq(gem.balanceOf(address(this)), 100 * days_vest, "17"); } function testFutureAccrual() public { @@ -676,6 +696,24 @@ contract DssVestTest is DSTest { assertEq(vat.sin(VOW), originalSin + 100 * days_vest * RAY); } + function testSuckableVestUsds() public { + uint256 originalSin = vat.sin(VOW); + uint256 id = sVestUsds.create(address(this), 100 * days_vest, block.timestamp, 100 days, 0, address(0)); + assertTrue(sVestUsds.valid(id)); + hevm.warp(block.timestamp + 1 days); + sVestUsds.vest(id); + assertEq(usds.balanceOf(address(this)), 1 * days_vest); + assertEq(vat.sin(VOW), originalSin + 1 * days_vest * RAY); + hevm.warp(block.timestamp + 9 days); + sVestUsds.vest(id); + assertEq(usds.balanceOf(address(this)), 10 * days_vest); + assertEq(vat.sin(VOW), originalSin + 10 * days_vest * RAY); + hevm.warp(block.timestamp + 365 days); + sVestUsds.vest(id); + assertEq(usds.balanceOf(address(this)), 100 * days_vest); + assertEq(vat.sin(VOW), originalSin + 100 * days_vest * RAY); + } + function testSuckableVestCaged() public { uint256 originalSin = vat.sin(VOW); uint256 id = sVest.create(address(this), 100 * days_vest, block.timestamp, 100 days, 0, address(0));