Skip to content

Commit

Permalink
add invariant and fuzz tests
Browse files Browse the repository at this point in the history
  • Loading branch information
trmid committed Oct 11, 2024
1 parent 3cbbe68 commit 1d90623
Show file tree
Hide file tree
Showing 5 changed files with 391 additions and 2 deletions.
8 changes: 8 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,11 @@ gas_reports = ["Foo"]
[rpc_endpoints]
world = "${WORLD_RPC_URL}"
worldSepolia = "${WORLD_SEPOLIA_RPC_URL}"

[fuzz]
runs = 10
max_test_rejects = 10000

[invariant]
runs = 10
max_test_rejects = 1000
146 changes: 146 additions & 0 deletions test/ERC4626Fuzz.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import { ERC4626Test, IMockERC20 } from "erc4626-tests/ERC4626.test.sol";

import { WorldIdVerifiedPrizeVaultWrapper, WorldIdVerifiedPrizeVault } from "./contracts/WorldIdVerifiedPrizeVaultWrapper.sol";
import { ERC4626, IERC4626, IERC20 } from "../lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC4626.sol";
import { TwabController } from "pt-v5-twab-controller/TwabController.sol";
import { PrizePool, ConstructorParams, IERC20 as PrizePoolIERC20 } from "pt-v5-prize-pool/PrizePool.sol";
import { ERC20Mock } from "../lib/openzeppelin-contracts/contracts/mocks/token/ERC20Mock.sol";
import { MockWorldIdAddressBook, IWorldIdAddressBook } from "./contracts/MockWorldIdAddressBook.sol";
import { Strings } from "openzeppelin/utils/Strings.sol";

contract ERC4626FuzzTest is ERC4626Test {

uint256 public currentTime;
address public currentActor;
address[] public actors;

WorldIdVerifiedPrizeVaultWrapper public worldVault;

MockWorldIdAddressBook public worldIdAddressBook;
TwabController public twabController;
PrizePool public prizePool;
ERC20Mock public prizeToken;

uint32 periodLength = 1 days;
uint32 periodOffset = 0;

uint256 public accountDepositLimit = 100e18;

address public alice;
address public bob;
address public nonVerifiedAddress;
address public claimer;
address public owner;

function setUp() public virtual override {
alice = makeAddr("alice");
bob = makeAddr("bob");
nonVerifiedAddress = makeAddr("nonVerifiedAddress");
claimer = makeAddr("claimer");
owner = makeAddr("owner");

worldIdAddressBook = new MockWorldIdAddressBook();
vm.startPrank(alice);
worldIdAddressBook.setAccountVerification(block.timestamp + 91 days);
vm.stopPrank();
vm.startPrank(bob);
worldIdAddressBook.setAccountVerification(block.timestamp + 91 days);
vm.stopPrank();

prizeToken = new ERC20Mock();
twabController = new TwabController(periodLength, periodOffset);
prizePool = new PrizePool(
ConstructorParams(
PrizePoolIERC20(address(prizeToken)),
twabController,
address(this),
0.5e18,
1 days,
uint48((block.timestamp / periodLength + 1) * periodLength),
91,
4,
100,
4,
30,
60
)
);
worldVault = new WorldIdVerifiedPrizeVaultWrapper(
"World Vault",
"przWLD",
prizePool,
worldIdAddressBook,
claimer,
owner,
accountDepositLimit
);

_delta_ = 0;
_vaultMayBeEmpty = true;
_unlimitedAmount = false;
_underlying_ = worldVault.asset();
_vault_ = address(worldVault);
}

/* ============ Override setup ============ */

function setUpVault(Init memory init) public virtual override {
for (uint256 i = 0; i < N; i++) {
init.user[i] = makeAddr(Strings.toString(i));
address user = init.user[i];

vm.assume(_isEOA(user));
uint256 shares = bound(init.share[i], 0, type(uint96).max);
if (shares % 2 == 0) {
// verify roughly half of accounts
vm.prank(user);
worldIdAddressBook.setAccountVerification(block.timestamp + 91 days);
try IMockERC20(_underlying_).mint(user, shares) {} catch {
vm.assume(false);
}

_approve(_underlying_, user, _vault_, shares);

vm.prank(user);
try IERC4626(_vault_).deposit(shares, user) {} catch {
vm.assume(false);
}
} else {
shares = 0;
}

uint256 assets = bound(init.asset[i], 0, type(uint256).max);
try IMockERC20(_underlying_).mint(user, assets) {} catch {
vm.assume(false);
}
}

setUpYield(init);
}

// No yield
function setUpYield(Init memory init) public virtual override { }

function _max_deposit(address from) internal virtual override returns (uint256) {
if (_unlimitedAmount) return type(uint96).max;
return bound(IERC20(_underlying_).balanceOf(from), 0, IERC4626(_vault_).maxDeposit(from) * 2);
}

function _max_mint(address from) internal virtual override returns (uint256) {
if (_unlimitedAmount) return type(uint96).max;
return bound(vault_convertToShares(IERC20(_underlying_).balanceOf(from)), 0, IERC4626(_vault_).maxMint(from) * 2);
}

function _max_withdraw(address from) internal virtual override returns (uint256) {
if (_unlimitedAmount) return type(uint96).max;
return vault_convertToAssets(IERC20(_vault_).balanceOf(from));
}

function _max_redeem(address from) internal virtual override returns (uint256) {
if (_unlimitedAmount) return type(uint96).max;
return IERC20(_vault_).balanceOf(from);
}
}
6 changes: 4 additions & 2 deletions test/WorldIdVerifiedPrizeVault.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ contract WorldIdVerifiedPrizeVaultTest is Test {
uint32 periodLength = 1 days;
uint32 periodOffset = 0;

uint256 accountDepositLimit = 100e18;

address alice;
address bob;
address nonVerifiedAddress;
Expand Down Expand Up @@ -72,7 +74,7 @@ contract WorldIdVerifiedPrizeVaultTest is Test {
worldIdAddressBook,
claimer,
owner,
100e18 // account deposit limit
accountDepositLimit
);
}

Expand Down Expand Up @@ -512,7 +514,7 @@ contract WorldIdVerifiedPrizeVaultTest is Test {

function testSetDepositLimit_isOwner() public {
vm.startPrank(owner);
assertEq(worldVault.accountDepositLimit(), 100e18);
assertEq(worldVault.accountDepositLimit(), accountDepositLimit);

vm.expectEmit();
emit SetAccountDepositLimit(10e18);
Expand Down
63 changes: 63 additions & 0 deletions test/WorldIdVerifiedPrizeVaultInvariant.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import { Test } from "forge-std/Test.sol";
import { WorldIdVerifiedPrizeVaultHarness, WorldIdVerifiedPrizeVaultWrapper, ERC20Mock } from "./contracts/WorldIdVerifiedPrizeVaultHarness.sol";

/// @dev This contract runs tests in a scenario where the yield vault can never lose funds (strictly increasing).
contract PrizeVaultInvariant is Test {
WorldIdVerifiedPrizeVaultHarness public vaultHarness;

modifier useCurrentTime() {
vm.warp(vaultHarness.currentTime());
_;
}

function setUp() external virtual {
vaultHarness = new WorldIdVerifiedPrizeVaultHarness();
targetContract(address(vaultHarness));
}

function invariantTotalSupplyLessThanOrEqualToTotalAssets() external useCurrentTime {
uint256 totalAssets = vaultHarness.worldVault().totalAssets();
uint256 totalSupply = vaultHarness.worldVault().totalSupply();
assertLe(totalSupply, totalAssets);
}

function invariantNonVerifiedWalletCantDeposit() external useCurrentTime {
address nonVerifiedAddress = vaultHarness.nonVerifiedAddress();
WorldIdVerifiedPrizeVaultWrapper worldVault = vaultHarness.worldVault();
ERC20Mock prizeToken = vaultHarness.prizeToken();
uint256 balance = prizeToken.balanceOf(nonVerifiedAddress);
if (balance > 0) {
vm.startPrank(nonVerifiedAddress);
prizeToken.approve(address(worldVault), balance);
vm.expectRevert();
worldVault.deposit(
balance,
nonVerifiedAddress
);
vm.stopPrank();
}
}

function invariantCantDepositMoreThanMax() external useCurrentTime {
address alice = vaultHarness.alice();
WorldIdVerifiedPrizeVaultWrapper worldVault = vaultHarness.worldVault();
if (vaultHarness.worldIdAddressBook().addressVerifiedUntil(alice) >= block.timestamp) {
uint256 balance = worldVault.balanceOf(alice);
uint256 maxDepositLimit = worldVault.accountDepositLimit();
if (balance >= maxDepositLimit) {
assertEq(worldVault.maxDeposit(alice), 0);
assertEq(worldVault.maxMint(alice), 0);
} else {
assertEq(worldVault.maxDeposit(alice), maxDepositLimit - balance);
assertEq(worldVault.maxMint(alice), maxDepositLimit - balance);
}
} else {
assertEq(worldVault.maxDeposit(alice), 0);
assertEq(worldVault.maxMint(alice), 0);
}
}

}
Loading

0 comments on commit 1d90623

Please sign in to comment.