Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

audit: boost unit test coverage, add invariant tests #12

Merged
merged 4 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion contracts/test/PointTokenVault.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -485,10 +485,16 @@ contract PointTokenVaultTest is Test {

rewardToken.mint(address(pointTokenVault), 3e18);

// Cannot redeem pTokens or convert rewards before redemption data is set
bytes32[] memory empty = new bytes32[](0);
vm.expectRevert(PointTokenVault.RewardsNotReleased.selector);
pointTokenVault.redeemRewards(PointTokenVault.Claim(eigenPointsId, 2e18, 2e18, empty), vitalik);
vm.expectRevert(PointTokenVault.RewardsNotReleased.selector);
pointTokenVault.convertRewardsToPTokens(vitalik, eigenPointsId, 1e18);

vm.prank(operator);
pointTokenVault.setRedemption(eigenPointsId, rewardToken, 2e18, false);

bytes32[] memory empty = new bytes32[](0);
vm.prank(vitalik);
pointTokenVault.redeemRewards(PointTokenVault.Claim(eigenPointsId, 2e18, 2e18, empty), vitalik);

Expand Down Expand Up @@ -603,6 +609,35 @@ contract PointTokenVaultTest is Test {
uint256 newBalance = address(pointTokenVault).balance;
assertEq(newBalance, initialBalance + amountToSend);
}

function test_PTokenNotDeployed() public {
// Deploy new instance of vault (without pToken deployed)
PointTokenVault mockVault = _deployAdditionalVault();

bytes32 root = 0x4e40a10ce33f33a4786960a8bb843fe0e170b651acd83da27abc97176c4bed3c;
bytes32[] memory proof = new bytes32[](1);
proof[0] = 0x6d0fcb8de12b1f57f81e49fa18b641487b932cdba4f064409fde3b05d3824ca2;

vm.prank(merkleUpdater);
mockVault.updateRoot(root);

// Cannot claim if pToken hasn't been deployed yet
vm.prank(vitalik);
vm.expectRevert(PointTokenVault.PTokenNotDeployed.selector);
mockVault.claimPTokens(PointTokenVault.Claim(eigenPointsId, 1e18, 1e18, proof), vitalik);
}

// Internal
function _deployAdditionalVault() internal returns (PointTokenVault mockVault) {
PointTokenVaultScripts scripts = new PointTokenVaultScripts();

mockVault = scripts.run("0.0.1");

mockVault.grantRole(pointTokenVault.DEFAULT_ADMIN_ROLE(), admin);
mockVault.grantRole(pointTokenVault.MERKLE_UPDATER_ROLE(), merkleUpdater);
mockVault.grantRole(pointTokenVault.OPERATOR_ROLE(), operator);
mockVault.revokeRole(pointTokenVault.DEFAULT_ADMIN_ROLE(), address(this));
}
}

contract Echo {
Expand Down
68 changes: 68 additions & 0 deletions contracts/test/invariant/PointTokenVaultInvariants.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
pragma solidity ^0.8.13;

import {Test, console, console2} from "forge-std/Test.sol";

import {MockPointTokenVault} from "../mock/MockPointTokenVault.sol";
import {MockPointTokenVaultScripts} from "../mock/script/MockPointTokenVault.s.sol";

import {PointTokenVaultHandler} from "./handlers/PointTokenVaultHandler.sol";

import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol";

contract PointTokenVaultInvariants is Test {
PointTokenVaultHandler handler;

MockPointTokenVault pointTokenVault;

function setUp() public {
// Mock vault bypasses merkle validation to allow for fuzzing.
// Merkle validation is tested in PointTokenVault.t.sol
MockPointTokenVaultScripts scripts = new MockPointTokenVaultScripts();

// Deploy the PointTokenVault
pointTokenVault = scripts.run("0.0.1");
address[3] memory admins = [
makeAddr("admin"),
makeAddr("operator"),
makeAddr("merkleUpdater")
];

pointTokenVault.grantRole(pointTokenVault.DEFAULT_ADMIN_ROLE(), admins[0]);
pointTokenVault.grantRole(pointTokenVault.MERKLE_UPDATER_ROLE(), admins[2]);
pointTokenVault.grantRole(pointTokenVault.OPERATOR_ROLE(), admins[1]);
pointTokenVault.revokeRole(pointTokenVault.DEFAULT_ADMIN_ROLE(), address(this));

handler = new PointTokenVaultHandler(
pointTokenVault,
admins
);

bytes4[] memory selectors = new bytes4[](5);
selectors[0] = handler.deposit.selector;
selectors[1] = handler.withdraw.selector;
selectors[2] = handler.claimPTokens.selector;
selectors[3] = handler.redeem.selector;
selectors[4] = handler.convertRewardsToPTokens.selector;

targetSelector(
FuzzSelector({
addr: address(handler),
selectors: selectors
})
);

targetContract(address(handler));
}

function invariant_point_earning_token_balances_remain_accurate_over_time() public view {
require(handler.checkPointEarningTokenGhosts(), "local pointsEarningTokens balances do not match balances stored in contract");
}

function invariant_claimed_ptoken_balances_remain_accurate_over_time() public view {
require(handler.checkClaimedPTokensGhosts(), "local claimed pTokens balances do not match balances stored in contract");
}

function invariant_ptoken_total_supplies_equal_sum_of_balances() public view {
require(handler.checkSumOfPTokenBalances(), "sum of a pToken's balances does not equal its total supply");
}
}
Loading