Skip to content

Commit

Permalink
Add cWETHv3 tests and generalize tests to work on assets with any dec…
Browse files Browse the repository at this point in the history
…imals (#13)

We generalize the tests even more to work with any decimals that Comet and the underlying asset may have. We also introduce tests for cWETHv3 on mainnet.
  • Loading branch information
kevincheng96 authored Oct 6, 2023
1 parent 1fb3863 commit 27714a8
Show file tree
Hide file tree
Showing 7 changed files with 255 additions and 212 deletions.
8 changes: 4 additions & 4 deletions test/BaseTest.t.sol → test/BaseUSDbCTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { CometWrapperTest } from "./CometWrapper.t.sol";
import { CometWrapperInvariantTest } from "./CometWrapperInvariant.t.sol";
import { RewardsTest } from "./Rewards.t.sol";

contract BaseTest is CometWrapperTest, CometWrapperInvariantTest, RewardsTest {
contract BaseUSDbCTest is CometWrapperTest, CometWrapperInvariantTest, RewardsTest {
string public override NETWORK = "base";
uint256 public override FORK_BLOCK_NUMBER = 4791144;

Expand All @@ -16,7 +16,7 @@ contract BaseTest is CometWrapperTest, CometWrapperInvariantTest, RewardsTest {
address public override CONFIGURATOR_ADDRESS = 0x45939657d1CA34A8FA39A924B71D28Fe8431e581;
address public override PROXY_ADMIN_ADDRESS = 0xbdE8F31D2DdDA895264e27DD990faB3DC87b372d;
address public override COMP_ADDRESS = 0x9e1028F5F1D5eDE59748FFceE5532509976840E0;
address public override USDC_ADDRESS = 0x4c80E24119CFB836cdF0a6b53dc23F04F7e652CA;
address public override USDC_HOLDER = 0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA;
address public override CUSDC_HOLDER = 0xBaC3100BEEE79CA34B18fbcD0437bd382Ee5611B;
address public override UNDERLYING_TOKEN_ADDRESS = 0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA;
address public override UNDERLYING_TOKEN_HOLDER = 0x4c80E24119CFB836cdF0a6b53dc23F04F7e652CA;
address public override COMET_HOLDER = 0xBaC3100BEEE79CA34B18fbcD0437bd382Ee5611B;
}
297 changes: 147 additions & 150 deletions test/CometWrapper.t.sol

Large diffs are not rendered by default.

24 changes: 8 additions & 16 deletions test/CometWrapperInvariant.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,11 @@ abstract contract CometWrapperInvariantTest is CoreTest, CometMath {
// - sum of all underlyingBalances of accounts <= totalAssets
// - sum of user balances == cometWrapper's principal in comet
function test_contractBalanceInvariants(uint256 amount1, uint256 amount2) public {
vm.assume(amount1 <= 2**48);
vm.assume(amount2 <= 2**48);
vm.assume(amount1 + amount2 < comet.balanceOf(cusdcHolder) - 100e6); // to account for borrowMin
vm.assume(amount1 > 100e6 && amount2 > 100e6);
(amount1, amount2) = setUpFuzzTestAssumptions(amount1, amount2);

vm.prank(cusdcHolder);
vm.prank(cometHolder);
comet.transfer(alice, amount1);
vm.prank(cusdcHolder);
vm.prank(cometHolder);
comet.transfer(bob, amount2);

uint256 aliceBalance = comet.balanceOf(alice);
Expand Down Expand Up @@ -82,11 +79,9 @@ abstract contract CometWrapperInvariantTest is CoreTest, CometMath {
// Invariants:
// - on redeem, decrease in wrapper's Comet principal == burnt user shares == change in total supply
function test_redeemInvariants(uint256 amount1) public {
vm.assume(amount1 <= 2**48);
vm.assume(amount1 > 1000e6);
vm.assume(amount1 < comet.balanceOf(cusdcHolder) - 100e6); // to account for borrowMin
amount1 = setUpFuzzTestAssumptions(amount1);

vm.prank(cusdcHolder);
vm.prank(cometHolder);
comet.transfer(alice, amount1);

skip(30000 days);
Expand Down Expand Up @@ -136,14 +131,11 @@ abstract contract CometWrapperInvariantTest is CoreTest, CometMath {
// - transfers must not change totalSupply
// - transfers must not change totalAssets
function test_transferInvariants(uint256 amount1, uint256 amount2) public {
vm.assume(amount1 <= 2**48);
vm.assume(amount2 <= 2**48);
vm.assume(amount1 + amount2 < comet.balanceOf(cusdcHolder));
vm.assume(amount1 > 1000e6 && amount2 > 1000e6);
(amount1, amount2) = setUpFuzzTestAssumptions(amount1, amount2);

vm.prank(cusdcHolder);
vm.prank(cometHolder);
comet.transfer(alice, amount1);
vm.prank(cusdcHolder);
vm.prank(cometHolder);
comet.transfer(bob, amount2);

vm.startPrank(alice);
Expand Down
72 changes: 56 additions & 16 deletions test/CoreTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,26 @@ abstract contract CoreTest is Test {
function CONFIGURATOR_ADDRESS() external virtual returns (address);
function PROXY_ADMIN_ADDRESS() external virtual returns (address);
function COMP_ADDRESS() external virtual returns (address);
function USDC_ADDRESS() external virtual returns (address);
function USDC_HOLDER() external virtual returns (address);
function CUSDC_HOLDER() external virtual returns (address);
function UNDERLYING_TOKEN_ADDRESS() external virtual returns (address);
function UNDERLYING_TOKEN_HOLDER() external virtual returns (address);
function COMET_HOLDER() external virtual returns (address);

address public cometAddress;
address public rewardAddress;
address public configuratorAddress;
address public proxyAdminAddress;
address public compAddress;
address public usdcHolder;
address public usdcAddress;
address public cusdcHolder;
address public underlyingTokenHolder;
address public underlyingTokenAddress;
address public cometHolder;

CometWrapper public cometWrapper;
CometInterface public comet;
ICometRewards public cometRewards;
ERC20 public usdc;
ERC20 public underlyingToken;
ERC20 public comp;
address public wrapperAddress;
uint256 public decimalScale;

address alice = address(0xABCD);
address bob = address(0xDCBA);
Expand All @@ -46,16 +47,55 @@ abstract contract CoreTest is Test {
configuratorAddress = this.CONFIGURATOR_ADDRESS();
proxyAdminAddress = this.PROXY_ADMIN_ADDRESS();
compAddress = this.COMP_ADDRESS();
usdcAddress = this.USDC_ADDRESS();
usdcHolder = this.USDC_HOLDER();
cusdcHolder = this.CUSDC_HOLDER();

usdc = ERC20(this.usdcAddress());
comp = ERC20(this.compAddress());
comet = CometInterface(this.cometAddress());
cometRewards = ICometRewards(this.rewardAddress());
underlyingTokenAddress = this.UNDERLYING_TOKEN_ADDRESS();
underlyingTokenHolder = this.UNDERLYING_TOKEN_HOLDER();
cometHolder = this.COMET_HOLDER();

underlyingToken = ERC20(underlyingTokenAddress);
comp = ERC20(compAddress);
comet = CometInterface(cometAddress);
cometRewards = ICometRewards(rewardAddress);
cometWrapper =
new CometWrapper(ERC20(this.cometAddress()), ICometRewards(this.rewardAddress()), "Wrapped Comet USDC", "WcUSDCv3");
new CometWrapper(ERC20(cometAddress), ICometRewards(rewardAddress), "Wrapped Comet UNDERLYING", "WcUNDERLYINGv3");
wrapperAddress = address(cometWrapper);
decimalScale = 10 ** underlyingToken.decimals();
}

function setUpFuzzTestAssumptions(uint256 amount) public view returns (uint256) {
string memory underlyingSymbol = underlyingToken.symbol();
uint256 minBorrow;
if (isEqual(underlyingSymbol, "USDC") || isEqual(underlyingSymbol, "USDbC")) {
minBorrow = 100 * decimalScale;
} else if (isEqual(underlyingSymbol, "WETH")) {
minBorrow = decimalScale / 10; // 0.1 WETH
} else {
revert("Unsupported underlying asset");
}

amount = bound(amount, minBorrow, comet.balanceOf(cometHolder) - minBorrow);
return amount;
}

function setUpFuzzTestAssumptions(uint256 amount1, uint256 amount2) public view returns (uint256, uint256) {
string memory underlyingSymbol = underlyingToken.symbol();
uint256 minBorrow;
if (isEqual(underlyingSymbol, "USDC") || isEqual(underlyingSymbol, "USDbC")) {
minBorrow = 100 * decimalScale;
amount1 = bound(amount1, minBorrow, 2**48);
amount2 = bound(amount2, minBorrow, 2**48);
} else if (isEqual(underlyingSymbol, "WETH")) {
minBorrow = decimalScale / 10; // 0.1 WETH
amount1 = bound(amount1, minBorrow, 2**88);
amount2 = bound(amount2, minBorrow, 2**88);
} else {
revert("Unsupported underlying asset");
}

vm.assume(amount1 + amount2 < comet.balanceOf(cometHolder) - minBorrow); // to account for borrowMin
return (amount1, amount2);
}

function isEqual(string memory s1, string memory s2) internal pure returns (bool) {
return keccak256(abi.encodePacked(s1)) == keccak256(abi.encodePacked(s2));
}
}
8 changes: 4 additions & 4 deletions test/MainnetTest.t.sol → test/MainnetUSDCTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { CometWrapperTest } from "./CometWrapper.t.sol";
import { CometWrapperInvariantTest } from "./CometWrapperInvariant.t.sol";
import { RewardsTest } from "./Rewards.t.sol";

contract MainnetTest is CometWrapperTest, CometWrapperInvariantTest, RewardsTest {
contract MainnetUSDCTest is CometWrapperTest, CometWrapperInvariantTest, RewardsTest {
string public override NETWORK = "mainnet";
uint256 public override FORK_BLOCK_NUMBER = 16617900;

Expand All @@ -16,7 +16,7 @@ contract MainnetTest is CometWrapperTest, CometWrapperInvariantTest, RewardsTest
address public override CONFIGURATOR_ADDRESS = 0x316f9708bB98af7dA9c68C1C3b5e79039cD336E3;
address public override PROXY_ADMIN_ADDRESS = 0x1EC63B5883C3481134FD50D5DAebc83Ecd2E8779;
address public override COMP_ADDRESS = 0xc00e94Cb662C3520282E6f5717214004A7f26888;
address public override USDC_ADDRESS = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
address public override USDC_HOLDER = 0x0A59649758aa4d66E25f08Dd01271e891fe52199;
address public override CUSDC_HOLDER = 0x638e9ad05DBd35B1c19dF3a4EAa0642A3B90A2AD;
address public override UNDERLYING_TOKEN_ADDRESS = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
address public override UNDERLYING_TOKEN_HOLDER = 0x0A59649758aa4d66E25f08Dd01271e891fe52199;
address public override COMET_HOLDER = 0x638e9ad05DBd35B1c19dF3a4EAa0642A3B90A2AD;
}
22 changes: 22 additions & 0 deletions test/MainnetWETHTest.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

import { Test } from "forge-std/Test.sol";
import { CometWrapper, CometInterface, ICometRewards, CometHelpers, ERC20 } from "../src/CometWrapper.sol";
import { CometWrapperTest } from "./CometWrapper.t.sol";
import { CometWrapperInvariantTest } from "./CometWrapperInvariant.t.sol";
import { RewardsTest } from "./Rewards.t.sol";

contract MainnetWETHTest is CometWrapperTest, CometWrapperInvariantTest, RewardsTest {
string public override NETWORK = "mainnet";
uint256 public override FORK_BLOCK_NUMBER = 18285773;

address public override COMET_ADDRESS = 0xA17581A9E3356d9A858b789D68B4d866e593aE94;
address public override REWARD_ADDRESS = 0x1B0e765F6224C21223AeA2af16c1C46E38885a40;
address public override CONFIGURATOR_ADDRESS = 0x316f9708bB98af7dA9c68C1C3b5e79039cD336E3;
address public override PROXY_ADMIN_ADDRESS = 0x1EC63B5883C3481134FD50D5DAebc83Ecd2E8779;
address public override COMP_ADDRESS = 0xc00e94Cb662C3520282E6f5717214004A7f26888;
address public override UNDERLYING_TOKEN_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; // WETH
address public override UNDERLYING_TOKEN_HOLDER = 0xF04a5cC80B1E94C69B48f5ee68a08CD2F09A7c3E; // WETH
address public override COMET_HOLDER = 0x10D88638Be3c26f3a47d861B8b5641508501035d; // cWETHv3
}
36 changes: 14 additions & 22 deletions test/Rewards.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ abstract contract RewardsTest is CoreTest {
function test_getRewardOwed(uint256 aliceAmount, uint256 bobAmount) public {
/* ===== Setup ===== */

vm.assume(aliceAmount <= 2**48);
vm.assume(bobAmount <= 2**48);
vm.assume(aliceAmount + bobAmount < comet.balanceOf(cusdcHolder) - 100e6); // to account for borrowMin
vm.assume(aliceAmount >= 2e6 && bobAmount >= 2e6);
(aliceAmount, bobAmount) = setUpFuzzTestAssumptions(aliceAmount, bobAmount);

enableRewardsAccrual();

Expand All @@ -21,7 +18,7 @@ abstract contract RewardsTest is CoreTest {
if (bobAmount % 2 != 0) bobAmount -= 1;

// Alice and Bob have same amount of funds in both CometWrapper and Comet
vm.startPrank(cusdcHolder);
vm.startPrank(cometHolder);
comet.transfer(alice, aliceAmount);
comet.transfer(bob, bobAmount);
vm.stopPrank();
Expand Down Expand Up @@ -80,7 +77,7 @@ abstract contract RewardsTest is CoreTest {
vm.etch(newRewardsAddr, code);

CometWrapper newCometWrapper =
new CometWrapper(ERC20(cometAddress), ICometRewards(newRewardsAddr), "Net Comet Wrapper", "NewWcUSDCv3");
new CometWrapper(ERC20(cometAddress), ICometRewards(newRewardsAddr), "New Comet Wrapper", "NewWcUNDERLYINGv3");

vm.expectRevert(CometWrapper.UninitializedReward.selector);
newCometWrapper.getRewardOwed(alice);
Expand All @@ -89,10 +86,7 @@ abstract contract RewardsTest is CoreTest {
function test_claimTo(uint256 aliceAmount, uint256 bobAmount) public {
/* ===== Setup ===== */

vm.assume(aliceAmount <= 2**48);
vm.assume(bobAmount <= 2**48);
vm.assume(aliceAmount + bobAmount < comet.balanceOf(cusdcHolder) - 100e6); // to account for borrowMin
vm.assume(aliceAmount >= 2e6 && bobAmount >= 2e6);
(aliceAmount, bobAmount) = setUpFuzzTestAssumptions(aliceAmount, bobAmount);

enableRewardsAccrual();
// Make sure CometRewards has ample COMP to claim
Expand All @@ -102,7 +96,7 @@ abstract contract RewardsTest is CoreTest {
if (aliceAmount % 2 != 0) aliceAmount -= 1;
if (bobAmount % 2 != 0) bobAmount -= 1;

vm.startPrank(cusdcHolder);
vm.startPrank(cometHolder);
comet.transfer(alice, aliceAmount);
comet.transfer(bob, bobAmount);
vm.stopPrank();
Expand Down Expand Up @@ -165,7 +159,7 @@ abstract contract RewardsTest is CoreTest {
vm.etch(newRewardsAddr, code);

CometWrapper newCometWrapper =
new CometWrapper(ERC20(cometAddress), ICometRewards(newRewardsAddr), "Net Comet Wrapper", "NewWcUSDCv3");
new CometWrapper(ERC20(cometAddress), ICometRewards(newRewardsAddr), "New Comet Wrapper", "NewWcUNDERLYINGv3");

vm.prank(alice);
vm.expectRevert(CometWrapper.UninitializedReward.selector);
Expand All @@ -175,16 +169,14 @@ abstract contract RewardsTest is CoreTest {
function test_accrueRewards(uint256 aliceAmount) public {
/* ===== Setup ===== */

vm.assume(aliceAmount <= 2**48);
vm.assume(aliceAmount < comet.balanceOf(cusdcHolder) - 100e6); // to account for borrowMin
vm.assume(aliceAmount >= 2e6);
aliceAmount = setUpFuzzTestAssumptions(aliceAmount);

enableRewardsAccrual();

// Make amount an even number so it can be divided equally by 2
if (aliceAmount % 2 != 0) aliceAmount -= 1;

vm.startPrank(cusdcHolder);
vm.startPrank(cometHolder);
comet.transfer(alice, aliceAmount);
vm.stopPrank();

Expand Down Expand Up @@ -223,7 +215,7 @@ abstract contract RewardsTest is CoreTest {
vm.prank(alice);
cometWrapper.transfer(bob, 5_000e6);

// Alice should have 30 days worth of accrued rewards for her 10K WcUSDC
// Alice should have 30 days worth of accrued rewards for her 10K WcUNDERLYING
assertEq(cometWrapper.getRewardOwed(alice), cometRewards.getRewardOwed(cometAddress, alice).owed);
// Bob should have no rewards accrued yet since his balance prior to the transfer was 0
assertEq(cometWrapper.getRewardOwed(bob), 0);
Expand All @@ -236,7 +228,7 @@ abstract contract RewardsTest is CoreTest {
vm.prank(alice);
cometWrapper.redeem(5_000e6, alice, alice);

// Alice should have 30 days worth of accrued rewards for her 10K WcUSDC and not for 5K WcUSDC
// Alice should have 30 days worth of accrued rewards for her 10K WcUNDERLYING and not for 5K WcUNDERLYING
assertEq(cometWrapper.getRewardOwed(alice), cometRewards.getRewardOwed(cometAddress, alice).owed);

vm.revertTo(snapshot);
Expand All @@ -247,7 +239,7 @@ abstract contract RewardsTest is CoreTest {
vm.prank(alice);
cometWrapper.withdraw(5_000e6, alice, alice);

// Alice should have 30 days worth of accrued rewards for her 10K WcUSDC and not for 5K WcUSDC
// Alice should have 30 days worth of accrued rewards for her 10K WcUNDERLYING and not for 5K WcUNDERLYING
assertEq(cometWrapper.getRewardOwed(alice), cometRewards.getRewardOwed(cometAddress, alice).owed);

vm.revertTo(snapshot);
Expand All @@ -258,7 +250,7 @@ abstract contract RewardsTest is CoreTest {
vm.prank(alice);
cometWrapper.mint(5_000e6, alice);

// Alice should have 30 days worth of accrued rewards for her 10K WcUSDC and not for 5K WcUSDC
// Alice should have 30 days worth of accrued rewards for her 10K WcUNDERLYING and not for 5K WcUNDERLYING
assertEq(cometWrapper.getRewardOwed(alice), cometRewards.getRewardOwed(cometAddress, alice).owed);

vm.revertTo(snapshot);
Expand All @@ -269,12 +261,12 @@ abstract contract RewardsTest is CoreTest {
vm.prank(alice);
cometWrapper.deposit(5_000e6, alice);

// Alice should have 30 days worth of accrued rewards for her 10K WcUSDC and not for 5K WcUSDC
// Alice should have 30 days worth of accrued rewards for her 10K WcUNDERLYING and not for 5K WcUNDERLYING
assertEq(cometWrapper.getRewardOwed(alice), cometRewards.getRewardOwed(cometAddress, alice).owed);
}

function setupAliceBalance() internal {
vm.prank(cusdcHolder);
vm.prank(cometHolder);
comet.transfer(alice, 20_000e6);
vm.startPrank(alice);
comet.allow(wrapperAddress, true);
Expand Down

0 comments on commit 27714a8

Please sign in to comment.