Created
February 19, 2024 10:13
-
-
Save Namaskar-1F64F/fb3787e89120acacee1ffb6837386620 to your computer and use it in GitHub Desktop.
Solmate tests
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
// SPDX-License-Identifier: GPL-3.0-only | |
pragma solidity ^0.8.20; | |
import "forge-std/Vm.sol"; | |
import "forge-std/console.sol"; | |
import "forge-std/Test.sol"; | |
import {GGPVault} from "../contracts/GGPVault.sol"; | |
import {MockTokenGGP} from "./mocks/MockTokenGGP.sol"; | |
import {MockStaking} from "./mocks/MockStaking.sol"; | |
import {MockStorage} from "./mocks/MockStorage.sol"; | |
import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; | |
contract GGPVaultTest is Test { | |
GGPVault vault; | |
MockTokenGGP ggpToken; | |
MockStaking mockStaking; | |
MockStorage mockStorage; | |
address owner; | |
address nodeOp1 = address(0x9); | |
event GGPCapUpdated(uint256 newCap); | |
event DepositedFromStaking(address indexed caller, uint256 amount); | |
error ERC4626ExceededMaxDeposit(address receiver, uint256 assets, uint256 max); | |
error OwnableUnauthorizedAccount(address account); | |
function setUp() public { | |
owner = address(this); | |
ggpToken = new MockTokenGGP(address(this)); | |
mockStaking = new MockStaking(ggpToken); | |
mockStorage = new MockStorage(); | |
mockStorage.setAddress(keccak256(abi.encodePacked("contract.address", "staking")), address(mockStaking)); | |
address proxy = Upgrades.deployUUPSProxy( | |
"GGPVault.sol", | |
abi.encodeCall(GGPVault.initialize, (address(ggpToken), address(mockStorage), address(this))) | |
); | |
vault = GGPVault(proxy); | |
} | |
function invariant_check() public {} | |
function testFuzzSingleMintRedeem(uint256 amount) public { | |
amount = bound(amount, uint256(0), vault.GGPCap()); | |
if (amount == 0) amount = 1; | |
uint256 aliceShareAmount = amount; | |
address alice = address(0xABCD); | |
ggpToken.transfer(alice, aliceShareAmount); | |
vm.prank(alice); | |
ggpToken.approve(address(vault), aliceShareAmount); | |
assertEq(ggpToken.allowance(alice, address(vault)), aliceShareAmount); | |
uint256 alicePreDepositBal = ggpToken.balanceOf(alice); | |
vm.prank(alice); | |
uint256 aliceUnderlyingAmount = vault.mint(aliceShareAmount, alice); | |
// Expect exchange rate to be 1:1 on initial mint. | |
assertEq(aliceShareAmount, aliceUnderlyingAmount); | |
assertEq(vault.previewWithdraw(aliceShareAmount), aliceUnderlyingAmount); | |
assertEq(vault.previewDeposit(aliceUnderlyingAmount), aliceShareAmount); | |
assertEq(vault.totalSupply(), aliceShareAmount); | |
assertEq(vault.totalAssets(), aliceUnderlyingAmount); | |
assertEq(vault.balanceOf(alice), aliceUnderlyingAmount); | |
assertEq(vault.convertToAssets(vault.balanceOf(alice)), aliceUnderlyingAmount); | |
assertEq(ggpToken.balanceOf(alice), alicePreDepositBal - aliceUnderlyingAmount); | |
vm.prank(alice); | |
vault.redeem(aliceShareAmount, alice, alice); | |
assertEq(vault.totalAssets(), 0); | |
assertEq(vault.balanceOf(alice), 0); | |
assertEq(vault.convertToAssets(vault.balanceOf(alice)), 0); | |
assertEq(ggpToken.balanceOf(alice), alicePreDepositBal); | |
} | |
function testFuzzSingleDepositWithdraw(uint256 amount) public { | |
amount = bound(amount, uint256(0), vault.GGPCap()); | |
if (amount == 0) amount = 1; | |
uint256 aliceUnderlyingAmount = amount; | |
address alice = address(0xABCD); | |
ggpToken.transfer(alice, aliceUnderlyingAmount); | |
vm.prank(alice); | |
ggpToken.approve(address(vault), aliceUnderlyingAmount); | |
assertEq(ggpToken.allowance(alice, address(vault)), aliceUnderlyingAmount); | |
uint256 alicePreDepositBal = ggpToken.balanceOf(alice); | |
vm.prank(alice); | |
uint256 aliceShareAmount = vault.deposit(aliceUnderlyingAmount, alice); | |
// Expect exchange rate to be 1:1 on initial deposit. | |
assertEq(aliceUnderlyingAmount, aliceShareAmount); | |
assertEq(vault.previewWithdraw(aliceShareAmount), aliceUnderlyingAmount); | |
assertEq(vault.previewDeposit(aliceUnderlyingAmount), aliceShareAmount); | |
assertEq(vault.totalSupply(), aliceShareAmount); | |
assertEq(vault.totalAssets(), aliceUnderlyingAmount); | |
assertEq(vault.balanceOf(alice), aliceShareAmount); | |
assertEq(vault.convertToAssets(vault.balanceOf(alice)), aliceUnderlyingAmount); | |
assertEq(ggpToken.balanceOf(alice), alicePreDepositBal - aliceUnderlyingAmount); | |
vm.prank(alice); | |
vault.withdraw(aliceUnderlyingAmount, alice, alice); | |
assertEq(vault.totalAssets(), 0); | |
assertEq(vault.balanceOf(alice), 0); | |
assertEq(vault.convertToAssets(vault.balanceOf(alice)), 0); | |
assertEq(ggpToken.balanceOf(alice), alicePreDepositBal); | |
} | |
function testMultipleMintDepositRedeemWithdraw() public { | |
// Scenario: | |
// A = Alice, B = Bob | |
// ________________________________________________________ | |
// | Vault shares | A share | A assets | B share | B assets | | |
// |========================================================| | |
// | 1. Alice mints 2000 shares (costs 2000 tokens) | | |
// |--------------|---------|----------|---------|----------| | |
// | 2000 | 2000 | 2000 | 0 | 0 | | |
// |--------------|---------|----------|---------|----------| | |
// | 2. Bob deposits 4000 tokens (mints 4000 shares) | | |
// |--------------|---------|----------|---------|----------| | |
// | 6000 | 2000 | 2000 | 4000 | 4000 | | |
// |--------------|---------|----------|---------|----------| | |
// | 3. Vault mutates by +3000 tokens... | | |
// | (simulated yield returned from strategy)... | | |
// |--------------|---------|----------|---------|----------| | |
// | 6000 | 2000 | 3000 | 4000 | 6000 | | |
// |--------------|---------|----------|---------|----------| | |
// | 4. Alice deposits 2000 tokens (mints 1333 shares) | | |
// |--------------|---------|----------|---------|----------| | |
// | 7333 | 3333 | 4999 | 4000 | 6000 | | |
// |--------------|---------|----------|---------|----------| | |
// | 5. Bob mints 2000 shares (costs 3001 assets) | | |
// | NOTE: Bob's assets spent got rounded up | | |
// | NOTE: Alice's vault assets got rounded up | | |
// |--------------|---------|----------|---------|----------| | |
// | 9333 | 3333 | 5000 | 6000 | 9000 | | |
// |--------------|---------|----------|---------|----------| | |
// | 6. Vault mutates by +3000 tokens... | | |
// | (simulated yield returned from strategy) | | |
// | NOTE: Vault holds 17001 tokens, but sum of | | |
// | assetsOf() is 17000. | | |
// |--------------|---------|----------|---------|----------| | |
// | 9333 | 3333 | 6071 | 6000 | 10929 | | |
// |--------------|---------|----------|---------|----------| | |
// | 7. Alice redeem 1333 shares (2428 assets) | | |
// |--------------|---------|----------|---------|----------| | |
// | 8000 | 2000 | 3643 | 6000 | 10929 | | |
// |--------------|---------|----------|---------|----------| | |
// | 8. Bob withdraws 2928 assets (1608 shares) | | |
// |--------------|---------|----------|---------|----------| | |
// | 6392 | 2000 | 3643 | 4392 | 8000 | | |
// |--------------|---------|----------|---------|----------| | |
// | 9. Alice withdraws 3643 assets (2000 shares) | | |
// | NOTE: Bob's assets have been rounded back up | | |
// |--------------|---------|----------|---------|----------| | |
// | 4392 | 0 | 0 | 4392 | 8001 | | |
// |--------------|---------|----------|---------|----------| | |
// | 10. Bob redeem 4392 shares (8001 tokens) | | |
// |--------------|---------|----------|---------|----------| | |
// | 0 | 0 | 0 | 0 | 0 | | |
// |______________|_________|__________|_________|__________| | |
address alice = address(0xABCD); | |
address bob = address(0xDCBA); | |
uint256 mutationUnderlyingAmount = 3000; | |
ggpToken.transfer(alice, 4000); | |
vm.prank(alice); | |
ggpToken.approve(address(vault), 4000); | |
assertEq(ggpToken.allowance(alice, address(vault)), 4000); | |
ggpToken.transfer(bob, 7001); | |
vm.prank(bob); | |
ggpToken.approve(address(vault), 7001); | |
assertEq(ggpToken.allowance(bob, address(vault)), 7001); | |
// 1. Alice mints 2000 shares (costs 2000 tokens) | |
vm.prank(alice); | |
uint256 aliceUnderlyingAmount = vault.mint(2000, alice); | |
uint256 aliceShareAmount = vault.previewDeposit(aliceUnderlyingAmount); | |
// Expect to have received the requested mint amount. | |
assertEq(aliceShareAmount, 2000); | |
assertEq(vault.balanceOf(alice), aliceShareAmount); | |
assertEq(vault.convertToAssets(vault.balanceOf(alice)), aliceUnderlyingAmount); | |
assertEq(vault.convertToShares(aliceUnderlyingAmount), vault.balanceOf(alice)); | |
// Expect a 1:1 ratio before mutation. | |
assertEq(aliceUnderlyingAmount, 2000); | |
// Sanity check. | |
assertEq(vault.totalSupply(), aliceShareAmount); | |
assertEq(vault.totalAssets(), aliceUnderlyingAmount); | |
// 2. Bob deposits 4000 tokens (mints 4000 shares) | |
vm.prank(bob); | |
uint256 bobShareAmount = vault.deposit(4000, bob); | |
uint256 bobUnderlyingAmount = vault.previewWithdraw(bobShareAmount); | |
// Expect to have received the requested underlying amount. | |
assertEq(bobUnderlyingAmount, 4000); | |
assertEq(vault.balanceOf(bob), bobShareAmount); | |
assertEq(vault.convertToAssets(vault.balanceOf(bob)), bobUnderlyingAmount); | |
assertEq(vault.convertToShares(bobUnderlyingAmount), vault.balanceOf(bob)); | |
// Expect a 1:1 ratio before mutation. | |
assertEq(bobShareAmount, bobUnderlyingAmount); | |
// Sanity check. | |
uint256 preMutationShareBal = aliceShareAmount + bobShareAmount; | |
uint256 preMutationBal = aliceUnderlyingAmount + bobUnderlyingAmount; | |
assertEq(vault.totalSupply(), preMutationShareBal); | |
assertEq(vault.totalAssets(), preMutationBal); | |
assertEq(vault.totalSupply(), 6000); | |
assertEq(vault.totalAssets(), 6000); | |
// 3. Vault mutates by +3000 tokens... | | |
// (simulated yield returned from strategy)... | |
// The Vault now contains more tokens than deposited which causes the exchange rate to change. | |
// Alice share is 33.33% of the Vault, Bob 66.66% of the Vault. | |
// Alice's share count stays the same but the underlying amount changes from 2000 to 3000. | |
// Bob's share count stays the same but the underlying amount changes from 4000 to 6000. | |
ggpToken.transfer(address(vault), mutationUnderlyingAmount); | |
assertEq(vault.totalSupply(), preMutationShareBal); | |
assertEq(vault.totalAssets(), preMutationBal + mutationUnderlyingAmount); | |
assertEq(vault.balanceOf(alice), aliceShareAmount); | |
assertEq( | |
vault.convertToAssets(vault.balanceOf(alice)), aliceUnderlyingAmount + (mutationUnderlyingAmount / 3) * 1 | |
); | |
assertEq(vault.balanceOf(bob), bobShareAmount); | |
assertEq(vault.convertToAssets(vault.balanceOf(bob)), bobUnderlyingAmount + (mutationUnderlyingAmount / 3) * 2); | |
// 4. Alice deposits 2000 tokens (mints 1333 shares) | |
vm.prank(alice); | |
vault.deposit(2000, alice); | |
assertEq(vault.totalSupply(), 7333); | |
assertEq(vault.balanceOf(alice), 3333); | |
assertEq(vault.convertToAssets(vault.balanceOf(alice)), 4999); | |
assertEq(vault.balanceOf(bob), 4000); | |
assertEq(vault.convertToAssets(vault.balanceOf(bob)), 6000); | |
// 5. Bob mints 2000 shares (costs 3001 assets) | |
// NOTE: Bob's assets spent got rounded up | |
// NOTE: Alices's vault assets got rounded up | |
vm.prank(bob); | |
vault.mint(2000, bob); | |
assertEq(vault.totalSupply(), 9333); | |
assertEq(vault.balanceOf(alice), 3333); | |
assertEq(vault.convertToAssets(vault.balanceOf(alice)), 5000); | |
assertEq(vault.balanceOf(bob), 6000); | |
assertEq(vault.convertToAssets(vault.balanceOf(bob)), 9000); | |
// Sanity checks: | |
// Alice and bob should have spent all their tokens now | |
assertEq(ggpToken.balanceOf(alice), 0); | |
assertEq(ggpToken.balanceOf(bob), 0); | |
// Assets in vault: 4k (alice) + 7k (bob) + 3k (yield) + 1 (round up) | |
assertEq(vault.totalAssets(), 14001); | |
// 6. Vault mutates by +3000 tokens | |
// NOTE: Vault holds 17001 tokens, but sum of assetsOf() is 17000. | |
ggpToken.transfer(address(vault), mutationUnderlyingAmount); | |
assertEq(vault.totalAssets(), 17001); | |
assertEq(vault.convertToAssets(vault.balanceOf(alice)), 6071); | |
assertEq(vault.convertToAssets(vault.balanceOf(bob)), 10929); | |
// 7. Alice redeem 1333 shares (2428 assets) | |
vm.prank(alice); | |
vault.redeem(1333, alice, alice); | |
assertEq(ggpToken.balanceOf(alice), 2428); | |
assertEq(vault.totalSupply(), 8000); | |
assertEq(vault.totalAssets(), 14573); | |
assertEq(vault.balanceOf(alice), 2000); | |
assertEq(vault.convertToAssets(vault.balanceOf(alice)), 3643); | |
assertEq(vault.balanceOf(bob), 6000); | |
assertEq(vault.convertToAssets(vault.balanceOf(bob)), 10929); | |
// 8. Bob withdraws 2929 assets (1608 shares) | |
vm.prank(bob); | |
vault.withdraw(2929, bob, bob); | |
assertEq(ggpToken.balanceOf(bob), 2929); | |
assertEq(vault.totalSupply(), 6392); | |
assertEq(vault.totalAssets(), 11644); | |
assertEq(vault.balanceOf(alice), 2000); | |
assertEq(vault.convertToAssets(vault.balanceOf(alice)), 3643); | |
assertEq(vault.balanceOf(bob), 4392); | |
assertEq(vault.convertToAssets(vault.balanceOf(bob)), 8000); | |
// 9. Alice withdraws 3643 assets (2000 shares) | |
// NOTE: Bob's assets have been rounded back up | |
vm.prank(alice); | |
vault.withdraw(3643, alice, alice); | |
assertEq(ggpToken.balanceOf(alice), 6071); | |
assertEq(vault.totalSupply(), 4392); | |
assertEq(vault.totalAssets(), 8001); | |
assertEq(vault.balanceOf(alice), 0); | |
assertEq(vault.convertToAssets(vault.balanceOf(alice)), 0); | |
assertEq(vault.balanceOf(bob), 4392); | |
assertEq(vault.convertToAssets(vault.balanceOf(bob)), 8001); | |
// 10. Bob redeem 4392 shares (8001 tokens) | |
vm.prank(bob); | |
vault.redeem(4392, bob, bob); | |
assertEq(ggpToken.balanceOf(bob), 10930); | |
assertEq(vault.totalSupply(), 0); | |
assertEq(vault.totalAssets(), 0); | |
assertEq(vault.balanceOf(alice), 0); | |
assertEq(vault.convertToAssets(vault.balanceOf(alice)), 0); | |
assertEq(vault.balanceOf(bob), 0); | |
assertEq(vault.convertToAssets(vault.balanceOf(bob)), 0); | |
// Sanity check | |
assertEq(ggpToken.balanceOf(address(vault)), 0); | |
} | |
function testFailDepositWithNotEnoughApproval() public { | |
ggpToken.transfer(address(this), 0.5e18); | |
ggpToken.approve(address(vault), 0.5e18); | |
assertEq(ggpToken.allowance(address(this), address(vault)), 0.5e18); | |
vault.deposit(1e18, address(this)); | |
} | |
function testFailWithdrawWithNotEnoughUnderlyingAmount() public { | |
ggpToken.transfer(address(this), 0.5e18); | |
ggpToken.approve(address(vault), 0.5e18); | |
vault.deposit(0.5e18, address(this)); | |
vault.withdraw(1e18, address(this), address(this)); | |
} | |
function testFailRedeemWithNotEnoughShareAmount() public { | |
ggpToken.transfer(address(this), 0.5e18); | |
ggpToken.approve(address(vault), 0.5e18); | |
vault.deposit(0.5e18, address(this)); | |
vault.redeem(1e18, address(this), address(this)); | |
} | |
function testFailWithdrawWithNoUnderlyingAmount() public { | |
vault.withdraw(1e18, address(this), address(this)); | |
} | |
function testFailRedeemWithNoShareAmount() public { | |
vault.redeem(1e18, address(this), address(this)); | |
} | |
function testFailDepositWithNoApproval() public { | |
vault.deposit(1e18, address(this)); | |
} | |
function testFailMintWithNoApproval() public { | |
vault.mint(1e18, address(this)); | |
} | |
function testMintZero() public { | |
vault.mint(0, address(this)); | |
assertEq(vault.balanceOf(address(this)), 0); | |
assertEq(vault.convertToAssets(vault.balanceOf(address(this))), 0); | |
assertEq(vault.totalSupply(), 0); | |
assertEq(vault.totalAssets(), 0); | |
} | |
function testWithdrawZero() public { | |
vault.withdraw(0, address(this), address(this)); | |
assertEq(vault.balanceOf(address(this)), 0); | |
assertEq(vault.convertToAssets(vault.balanceOf(address(this))), 0); | |
assertEq(vault.totalSupply(), 0); | |
assertEq(vault.totalAssets(), 0); | |
} | |
function testVaultInteractionsForSomeoneElse() public { | |
// init 2 users with a 1e18 balance | |
address alice = address(0xABCD); | |
address bob = address(0xDCBA); | |
ggpToken.transfer(alice, 1e18); | |
ggpToken.transfer(bob, 1e18); | |
vm.prank(alice); | |
ggpToken.approve(address(vault), 1e18); | |
vm.prank(bob); | |
ggpToken.approve(address(vault), 1e18); | |
// alice deposits 1e18 for bob | |
vm.prank(alice); | |
vault.deposit(1e18, bob); | |
assertEq(vault.balanceOf(alice), 0); | |
assertEq(vault.balanceOf(bob), 1e18); | |
assertEq(ggpToken.balanceOf(alice), 0); | |
// bob mint 1e18 for alice | |
vm.prank(bob); | |
vault.mint(1e18, alice); | |
assertEq(vault.balanceOf(alice), 1e18); | |
assertEq(vault.balanceOf(bob), 1e18); | |
assertEq(ggpToken.balanceOf(bob), 0); | |
// alice redeem 1e18 for bob | |
vm.prank(alice); | |
vault.redeem(1e18, bob, alice); | |
assertEq(vault.balanceOf(alice), 0); | |
assertEq(vault.balanceOf(bob), 1e18); | |
assertEq(ggpToken.balanceOf(bob), 1e18); | |
// bob withdraw 1e18 for alice | |
vm.prank(bob); | |
vault.withdraw(1e18, alice, bob); | |
assertEq(vault.balanceOf(alice), 0); | |
assertEq(vault.balanceOf(bob), 0); | |
assertEq(ggpToken.balanceOf(alice), 1e18); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment