-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
adds accounting and compliance tests
- Loading branch information
Showing
2 changed files
with
278 additions
and
0 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,184 @@ | ||
// SPDX-License-Identifier: BSD-3-Clause | ||
pragma solidity ^0.8.24; | ||
|
||
import {Test} from "lib/forge-std/src/Test.sol"; | ||
import {Vault} from "src/Vault.sol"; | ||
import {TransparentUpgradeableProxy} from "src/Common.sol"; | ||
import {MainnetContracts} from "script/Contracts.sol"; | ||
import {MainnetActors} from "script/Actors.sol"; | ||
import {Etches} from "test/helpers/Etches.sol"; | ||
import {WETH9} from "test/mocks/MockWETH.sol"; | ||
import {SetupVault} from "test/helpers/SetupVault.sol"; | ||
|
||
contract VaultAccountingUnitTest is Test, MainnetContracts, MainnetActors, Etches { | ||
Vault public vaultImplementation; | ||
TransparentUpgradeableProxy public vaultProxy; | ||
|
||
Vault public vault; | ||
WETH9 public weth; | ||
|
||
address public alice = address(0x1); | ||
uint256 public constant INITIAL_BALANCE = 100_000 ether; | ||
|
||
function setUp() public { | ||
SetupVault setupVault = new SetupVault(); | ||
(vault, weth) = setupVault.setup(); | ||
|
||
// Give Alice some tokens | ||
deal(alice, INITIAL_BALANCE); | ||
weth.deposit{value: INITIAL_BALANCE}(); | ||
weth.transfer(alice, INITIAL_BALANCE); | ||
|
||
// Approve vault to spend Alice's tokens | ||
vm.prank(alice); | ||
weth.approve(address(vault), type(uint256).max); | ||
} | ||
|
||
function allocateToBuffer(uint256 amount) public { | ||
address[] memory targets = new address[](2); | ||
targets[0] = WETH; | ||
targets[1] = BUFFER_STRATEGY; | ||
|
||
uint256[] memory values = new uint256[](2); | ||
values[0] = 0; | ||
values[1] = 0; | ||
|
||
bytes[] memory data = new bytes[](2); | ||
data[0] = abi.encodeWithSignature("approve(address,uint256)", vault.bufferStrategy(), amount); | ||
data[1] = abi.encodeWithSignature("deposit(uint256,address)", amount, address(vault)); | ||
|
||
vm.prank(ADMIN); | ||
vault.processor(targets, values, data); | ||
} | ||
|
||
function test_Vault_Accounting_convertToShares() public view { | ||
uint256 assets = 1000 ether; | ||
uint256 shares = vault.convertToShares(assets); | ||
assertEq(shares, vault.previewDeposit(assets), "Shares should match previewDeposit"); | ||
} | ||
|
||
function test_Vault_Accounting_convertToAssets() public view { | ||
uint256 shares = 1000 ether; | ||
uint256 assets = vault.convertToAssets(shares); | ||
assertEq(assets, vault.previewRedeem(shares), "Assets should match previewRedeem"); | ||
} | ||
|
||
function test_Vault_Accounting_totalAssets_afterDeposit() public { | ||
uint256 depositAmount = 1000 ether; | ||
vm.prank(alice); | ||
vault.deposit(depositAmount, alice); | ||
uint256 totalAssets = vault.totalAssets(); | ||
assertEq(totalAssets, depositAmount, "Total assets should match the deposit amount"); | ||
} | ||
|
||
function test_Vault_Accounting_totalAssets_afterMultipleDeposits() public { | ||
uint256 depositAmount1 = 1000 ether; | ||
uint256 depositAmount2 = 2000 ether; | ||
vm.prank(alice); | ||
vault.deposit(depositAmount1, alice); | ||
vm.prank(alice); | ||
vault.deposit(depositAmount2, alice); | ||
uint256 totalAssets = vault.totalAssets(); | ||
assertEq(totalAssets, depositAmount1 + depositAmount2, "Total assets should match the sum of deposit amounts"); | ||
} | ||
|
||
function test_Vault_Accounting_totalAssets_afterWithdraw() public { | ||
uint256 depositAmount = 1000 ether; | ||
uint256 withdrawAmount = 500 ether; | ||
vm.prank(alice); | ||
vault.deposit(depositAmount, alice); | ||
|
||
allocateToBuffer(depositAmount); | ||
|
||
vm.prank(alice); | ||
vault.withdraw(withdrawAmount, alice, alice); | ||
uint256 totalAssets = vault.totalAssets(); | ||
assertEq( | ||
totalAssets, | ||
depositAmount - withdrawAmount, | ||
"Total assets should match the remaining amount after withdrawal" | ||
); | ||
} | ||
|
||
function test_Vault_Accounting_totalAssets_afterMultipleWithdrawals() public { | ||
uint256 depositAmount = 3000 ether; | ||
uint256 withdrawAmount1 = 1000 ether; | ||
uint256 withdrawAmount2 = 500 ether; | ||
vm.prank(alice); | ||
vault.deposit(depositAmount, alice); | ||
|
||
allocateToBuffer(depositAmount); | ||
|
||
vm.prank(alice); | ||
vault.withdraw(withdrawAmount1, alice, alice); | ||
vm.prank(alice); | ||
vault.withdraw(withdrawAmount2, alice, alice); | ||
uint256 totalAssets = vault.totalAssets(); | ||
assertEq( | ||
totalAssets, | ||
depositAmount - withdrawAmount1 - withdrawAmount2, | ||
"Total assets should match the remaining amount after multiple withdrawals" | ||
); | ||
} | ||
|
||
function test_Vault_Accounting_totalSupply_afterDeposit() public { | ||
uint256 depositAmount = 1000 ether; | ||
vm.prank(alice); | ||
vault.deposit(depositAmount, alice); | ||
uint256 totalSupply = vault.totalSupply(); | ||
assertEq(totalSupply, depositAmount, "Total supply should match the deposit amount"); | ||
} | ||
|
||
function test_Vault_Accounting_totalSupply_afterMultipleDeposits() public { | ||
uint256 depositAmount1 = 1000 ether; | ||
uint256 depositAmount2 = 2000 ether; | ||
vm.prank(alice); | ||
vault.deposit(depositAmount1, alice); | ||
vm.prank(alice); | ||
vault.deposit(depositAmount2, alice); | ||
uint256 totalSupply = vault.totalSupply(); | ||
assertEq(totalSupply, depositAmount1 + depositAmount2, "Total supply should match the sum of deposit amounts"); | ||
} | ||
|
||
function test_Vault_Accounting_totalSupply_afterWithdraw() public { | ||
uint256 depositAmount = 1000 ether; | ||
uint256 bufferRatio = 5; | ||
|
||
vm.prank(alice); | ||
vault.deposit(depositAmount, alice); | ||
|
||
allocateToBuffer(depositAmount / bufferRatio); | ||
|
||
vm.prank(alice); | ||
uint256 maxWithdraw = vault.maxWithdraw(alice); | ||
vault.withdraw(maxWithdraw, alice, alice); | ||
uint256 totalSupply = vault.totalSupply(); | ||
assertEq( | ||
totalSupply, depositAmount - maxWithdraw, "Total supply should match the remaining amount after withdrawal" | ||
); | ||
} | ||
|
||
function test_Vault_Accounting_totalSupply_afterMultipleWithdrawals() public { | ||
uint256 depositAmount = 3000 ether; | ||
uint256 bufferRatio = 5; | ||
|
||
uint256 withdrawAmount1 = vault.maxWithdraw(alice) / 3; | ||
uint256 withdrawAmount2 = vault.maxWithdraw(alice) / 7; | ||
|
||
vm.prank(alice); | ||
vault.deposit(depositAmount, alice); | ||
|
||
allocateToBuffer(depositAmount / bufferRatio); | ||
|
||
vm.prank(alice); | ||
vault.withdraw(withdrawAmount1, alice, alice); | ||
vm.prank(alice); | ||
vault.withdraw(withdrawAmount2, alice, alice); | ||
uint256 totalSupply = vault.totalSupply(); | ||
assertEq( | ||
totalSupply, | ||
depositAmount - withdrawAmount1 - withdrawAmount2, | ||
"Total supply should match the remaining amount after multiple withdrawals" | ||
); | ||
} | ||
} |
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,94 @@ | ||
// SPDX-License-Identifier: BSD-3-Clause | ||
pragma solidity ^0.8.24; | ||
|
||
import {Test} from "lib/forge-std/src/Test.sol"; | ||
import {Vault} from "src/Vault.sol"; | ||
import {TransparentUpgradeableProxy} from "src/Common.sol"; | ||
import {MainnetContracts} from "script/Contracts.sol"; | ||
import {MainnetActors} from "script/Actors.sol"; | ||
import {Etches} from "test/helpers/Etches.sol"; | ||
import {WETH9} from "test/mocks/MockWETH.sol"; | ||
import {SetupVault} from "test/helpers/SetupVault.sol"; | ||
|
||
contract Vault4626ComplianceUnitTest is Test, MainnetContracts, MainnetActors, Etches { | ||
Vault public vaultImplementation; | ||
TransparentUpgradeableProxy public vaultProxy; | ||
|
||
Vault public vault; | ||
WETH9 public weth; | ||
|
||
address public alice = address(0x1); | ||
uint256 public constant INITIAL_BALANCE = 100_000 ether; | ||
|
||
function setUp() public { | ||
SetupVault setupVault = new SetupVault(); | ||
(vault, weth) = setupVault.setup(); | ||
|
||
// Give Alice some tokens | ||
deal(alice, INITIAL_BALANCE); | ||
weth.deposit{value: INITIAL_BALANCE}(); | ||
weth.transfer(alice, INITIAL_BALANCE); | ||
|
||
// Approve vault to spend Alice's tokens | ||
vm.prank(alice); | ||
weth.approve(address(vault), type(uint256).max); | ||
} | ||
|
||
/* The maxWithdraw function should return the maximum amount of underlying assets that | ||
can be withdrawn by the owner without affecting the vault's ability to meet its obligations. | ||
Paused State: The function correctly returns 0 if the vault is paused | ||
*/ | ||
function test_Vault_Compliance_maxWithdraw_paused() public { | ||
vm.prank(ADMIN); | ||
vault.pause(true); | ||
uint256 maxWithdrawAmount = vault.maxWithdraw(alice); | ||
assertEq(maxWithdrawAmount, 0, "Max withdraw amount should be 0 when paused"); | ||
} | ||
|
||
/* Asset Conversion: The function calculates the baseConvertedAssets by converting the | ||
owner's balance of vault tokens to the equivalent amount of underlying assets. | ||
This is consistent with the ERC-4626 requirement to determine the maximum withdrawable | ||
amount based on the owner's share balance. | ||
*/ | ||
function test_Vault_Compliance_maxWithdraw_assetConversion() public view { | ||
uint256 aliceBalance = vault.balanceOf(alice); | ||
uint256 baseConvertedAssets = vault.convertToAssets(aliceBalance); | ||
uint256 maxWithdrawAmount = vault.maxWithdraw(alice); | ||
assertEq(maxWithdrawAmount, baseConvertedAssets, "Max withdraw amount should be equal to base converted assets"); | ||
} | ||
|
||
/* | ||
Available Assets Check: The function checks the available assets in the buffer strategy | ||
and ensures that the withdrawal does not exceed this amount. This is a prudent measure | ||
to ensure the vault maintains sufficient liquidity. | ||
*/ | ||
function test_Vault_Compliance_maxWithdraw_availableAssetsCheck() public view { | ||
uint256 aliceBalance = vault.balanceOf(alice); | ||
uint256 baseConvertedAssets = vault.convertToAssets(aliceBalance); | ||
uint256 availableAssets = vault.maxWithdraw(address(vault)); | ||
uint256 maxWithdrawAmount = vault.maxWithdraw(alice); | ||
assertEq( | ||
maxWithdrawAmount, | ||
availableAssets < baseConvertedAssets ? 0 : baseConvertedAssets, | ||
"Max withdraw amount should be the lesser of available assets or base converted assets" | ||
); | ||
} | ||
|
||
/* | ||
5. Return Value: The function returns the lesser of the converted asset value or the available assets, | ||
which aligns with the intent of ensuring that the vault can fulfill the withdrawal request without | ||
compromising its liquidity. | ||
*/ | ||
function test_Vault_Compliance_maxWithdraw_returnValue() public view { | ||
uint256 aliceBalance = vault.balanceOf(alice); | ||
uint256 baseConvertedAssets = vault.convertToAssets(aliceBalance); | ||
uint256 availableAssets = vault.maxWithdraw(address(vault)); | ||
uint256 maxWithdrawAmount = vault.maxWithdraw(alice); | ||
uint256 expectedValue = availableAssets < baseConvertedAssets ? 0 : baseConvertedAssets; | ||
assertEq( | ||
maxWithdrawAmount, | ||
expectedValue, | ||
"Max withdraw amount should be the lesser of available assets or base converted assets" | ||
); | ||
} | ||
} |