-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
190 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
"extends": "solhint:recommended", | ||
"plugins": [], | ||
"rules": { | ||
"avoid-suicide": "error", | ||
"avoid-sha3": "warn", | ||
"compiler-version": ["error", "^0.8.0"], | ||
"func-visibility": ["warn", { "ignoreConstructors": true }], | ||
"reason-string": ["warn", { "maxLength": 64 }], | ||
"not-rely-on-time": "warn", | ||
"state-visibility": "error", | ||
"max-line-length": ["warn", 120], | ||
"no-console": "off" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// SPDX-License-Identifier: BSD-3-Clause | ||
pragma solidity ^0.8.24; | ||
|
||
import {SingleVault} from "src/SingleVault.sol"; | ||
|
||
contract MockSingleVault is SingleVault { | ||
constructor() { | ||
_disableInitializers(); | ||
} | ||
|
||
uint256 public constant R_TWO_D = 2; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
// SPDX-License-Identifier: BSD-3-Clause | ||
pragma solidity ^0.8.24; | ||
|
||
import {SingleVault} from "src/SingleVault.sol"; | ||
import {MockERC20} from "test/mocks/MockERC20.sol"; | ||
import {Math} from "src/Common.sol"; | ||
import {SetupHelper} from "test/helpers/Setup.sol"; | ||
import {Etches} from "test/helpers/Etches.sol"; | ||
import {TestConstants} from "test/helpers/Constants.sol"; | ||
import {LocalActors} from "script/Actors.sol"; | ||
|
||
import "forge-std/Test.sol"; | ||
|
||
contract SingleInvariantTests is Test, LocalActors, TestConstants { | ||
using Math for uint256; | ||
|
||
SingleVault public vault; | ||
MockERC20 public asset; | ||
address USER = address(33); | ||
|
||
function setUp() public { | ||
vm.startPrank(ADMIN); | ||
asset = MockERC20(address(new MockERC20(ASSET_NAME, ASSET_SYMBOL))); | ||
|
||
Etches etches = new Etches(); | ||
etches.mockListaStakeManager(); | ||
|
||
SetupHelper setup = new SetupHelper(); | ||
vault = setup.createVault(asset); | ||
} | ||
|
||
event Log(uint256 amount, string name); | ||
|
||
function test_totalAssetsAlwaysCorrect(uint256 depositAmount) public { | ||
if (depositAmount < 1) return; | ||
if (depositAmount > 1e50) return; | ||
|
||
uint256 initialTotalAssets = vault.totalAssets(); | ||
depositHelper(USER, depositAmount); | ||
uint256 currentTotalAssets = vault.totalAssets(); | ||
uint256 expectedDepositAssets = vault.previewDeposit(depositAmount); | ||
uint256 expectedAssets = initialTotalAssets + expectedDepositAssets; | ||
assertClose(currentTotalAssets, expectedAssets, 10, "Total assets mismatch after deposit"); | ||
} | ||
|
||
function test_totalSupplyAlwaysCorrect(uint256 depositAmount) public { | ||
if (depositAmount < 1) return; | ||
if (depositAmount > 1e50) return; | ||
|
||
uint256 initialTotalSupply = vault.totalSupply(); | ||
depositHelper(USER, depositAmount); | ||
uint256 currentTotalSupply = vault.totalSupply(); | ||
uint256 expectedDepositAssets = vault.previewDeposit(depositAmount); | ||
uint256 expectedSupply = initialTotalSupply + expectedDepositAssets; | ||
assertClose(currentTotalSupply, expectedSupply, 1, "Total supply mismatch after deposit"); | ||
} | ||
|
||
function test_totalSupplyMatchesBalances(uint256 depositAmount) public { | ||
if (depositAmount < 2) return; | ||
if (depositAmount > 1e50) return; | ||
|
||
depositHelper(USER, depositAmount); | ||
uint256 total = vault.totalSupply(); | ||
assertEq(vault.convertToShares(depositAmount + 1 ether), total, "Total supply does not balances"); | ||
} | ||
|
||
function test_conversionConsistency(uint256 depositAmount) public view { | ||
if (depositAmount < 2) return; | ||
if (depositAmount > 1e50) return; | ||
uint256 shares = vault.convertToShares(depositAmount); | ||
uint256 convertedAssets = vault.convertToAssets(shares); | ||
assertClose(depositAmount, convertedAssets, 1, "Conversion inconsistency"); | ||
} | ||
|
||
function depositHelper(address user, uint256 depositAmount) public { | ||
vm.startPrank(user); | ||
asset.mint(depositAmount); | ||
asset.approve(address(vault), depositAmount); | ||
vault.deposit(depositAmount, address(this)); | ||
vm.stopPrank(); | ||
} | ||
|
||
function assertClose(uint256 actual, uint256 expected, uint256 delta, string memory message) internal pure { | ||
require(actual >= expected - delta && actual <= expected + delta, message); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
// BSD 3-Clause License | ||
pragma solidity ^0.8.24; | ||
|
||
import {SingleVault} from "src/SingleVault.sol"; | ||
import {MockERC20} from "test/mocks/MockERC20.sol"; | ||
import {SetupHelper} from "test/helpers/Setup.sol"; | ||
import {Etches} from "test/helpers/Etches.sol"; | ||
import {LocalActors} from "script/Actors.sol"; | ||
import {TestConstants} from "test/helpers/Constants.sol"; | ||
import {IERC20} from "src/Common.sol"; | ||
import {IVaultFactory} from "src/IVaultFactory.sol"; | ||
import {MockSingleVault} from "test/mocks/MockSingleVault.sol"; | ||
import {TimelockController} from "src/Common.sol"; | ||
|
||
import "forge-std/Test.sol"; | ||
|
||
contract SingleVaultUpgradeTests is Test, LocalActors, TestConstants { | ||
SingleVault public vault; | ||
MockERC20 public asset; | ||
IVaultFactory public factory; | ||
|
||
function setUp() public { | ||
vm.startPrank(ADMIN); | ||
asset = MockERC20(address(new MockERC20(ASSET_NAME, ASSET_SYMBOL))); | ||
|
||
Etches etches = new Etches(); | ||
etches.mockListaStakeManager(); | ||
|
||
SetupHelper setup = new SetupHelper(); | ||
vault = setup.createVault(asset); | ||
|
||
factory = IVaultFactory(setup.factory()); | ||
} | ||
|
||
function testUpgrade() public { | ||
vm.startPrank(ADMIN); | ||
SingleVault newVault = new MockSingleVault(); | ||
|
||
// the timelock on the factory is the admin for proxy upgrades | ||
TimelockController timelock = TimelockController(payable(factory.timelock())); | ||
|
||
// schedule a proxy upgrade transaction on the timelock | ||
// the traget is the proxy admin, created by foundry test | ||
address target = 0xF094c1B2ec3E52f6D02603C4dB28dd4Ba0067048; | ||
uint256 value = 0; | ||
|
||
bytes4 selector = bytes4(keccak256("upgradeAndCall(address,address,bytes)")); | ||
|
||
bytes memory data = abi.encodeWithSelector(selector, address(vault), address(newVault), ""); | ||
|
||
bytes32 predecessor = bytes32(0); | ||
bytes32 salt = keccak256("chad"); | ||
|
||
uint256 delay = 1; | ||
vm.startPrank(PROPOSER_1); | ||
timelock.schedule(target, value, data, predecessor, salt, delay); | ||
vm.stopPrank(); | ||
|
||
bytes32 id = keccak256(abi.encode(target, value, data, predecessor, salt)); | ||
assert(timelock.getOperationState(id) == TimelockController.OperationState.Waiting); | ||
|
||
assertEq(timelock.isOperationReady(id), false); | ||
assertEq(timelock.isOperationDone(id), false); | ||
assertEq(timelock.isOperation(id), true); | ||
|
||
//execute the transaction | ||
vm.warp(500); | ||
vm.startPrank(EXECUTOR_1); | ||
timelock.execute(target, value, data, predecessor, salt); | ||
|
||
// Verify the transaction was executed successfully | ||
assertEq(timelock.isOperationReady(id), false); | ||
assertEq(timelock.isOperationDone(id), true); | ||
assert(timelock.getOperationState(id) == TimelockController.OperationState.Done); | ||
} | ||
} |