166 lines
5.6 KiB
Solidity
166 lines
5.6 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity 0.8.35;
|
|
|
|
import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
|
|
import {Options, Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";
|
|
|
|
import {BankV1} from "../src/BankV1.sol";
|
|
import {BankV2} from "../src/BankV2.sol";
|
|
import {BankTestBase} from "./helpers/BankTestBase.sol";
|
|
|
|
event BalanceTransferred(address indexed from, address indexed to, uint256 amount);
|
|
|
|
contract BankV2Test is BankTestBase {
|
|
uint256 private constant ALICE_DEPOSIT = 1_000e6;
|
|
uint256 private constant BOB_DEPOSIT = 500e6;
|
|
|
|
BankV2 internal bankV2;
|
|
|
|
function setUp() public override {
|
|
super.setUp();
|
|
_deposit(alice, ALICE_DEPOSIT);
|
|
_deposit(bob, BOB_DEPOSIT);
|
|
|
|
Options memory opts;
|
|
opts.referenceContract = "BankV1.sol:BankV1";
|
|
Upgrades.upgradeProxy(proxy, "BankV2.sol:BankV2", "", opts, owner);
|
|
bankV2 = BankV2(proxy);
|
|
}
|
|
|
|
function testTransferBalanceMoves250MillionUnitsAndEmitsExactEvent() public {
|
|
vm.expectEmit(true, true, false, true, proxy);
|
|
emit BalanceTransferred(alice, bob, 250e6);
|
|
vm.prank(alice);
|
|
bankV2.transferBalance(bob, 250e6);
|
|
|
|
assertEq(bankV2.balanceOf(alice), 750e6);
|
|
assertEq(bankV2.balanceOf(bob), 750e6);
|
|
}
|
|
|
|
function testTransferBalanceLeavesLiabilitiesAndTokenReservesUnchanged() public {
|
|
uint256 liabilitiesBefore = bankV2.totalLiabilities();
|
|
uint256 reservesBefore = token.balanceOf(proxy);
|
|
|
|
vm.prank(alice);
|
|
bankV2.transferBalance(bob, 250e6);
|
|
|
|
assertEq(bankV2.totalLiabilities(), liabilitiesBefore);
|
|
assertEq(token.balanceOf(proxy), reservesBefore);
|
|
}
|
|
|
|
function testTransferBalanceRejectsZeroAmount() public {
|
|
vm.prank(alice);
|
|
vm.expectRevert(BankV1.ZeroAmount.selector);
|
|
bankV2.transferBalance(bob, 0);
|
|
}
|
|
|
|
function testTransferBalanceRejectsZeroRecipient() public {
|
|
vm.prank(alice);
|
|
vm.expectRevert(abi.encodeWithSelector(BankV2.InvalidRecipient.selector, address(0)));
|
|
bankV2.transferBalance(address(0), 1);
|
|
}
|
|
|
|
function testTransferBalanceRejectsSenderAsRecipient() public {
|
|
vm.prank(alice);
|
|
vm.expectRevert(BankV2.SelfTransfer.selector);
|
|
bankV2.transferBalance(alice, 1);
|
|
}
|
|
|
|
function testTransferBalanceReportsAvailableAndRequestedWhenBalanceIsInsufficient() public {
|
|
vm.prank(alice);
|
|
vm.expectRevert(
|
|
abi.encodeWithSelector(BankV1.InsufficientBalance.selector, alice, ALICE_DEPOSIT, ALICE_DEPOSIT + 1)
|
|
);
|
|
bankV2.transferBalance(bob, ALICE_DEPOSIT + 1);
|
|
|
|
assertEq(bankV2.balanceOf(alice), ALICE_DEPOSIT);
|
|
assertEq(bankV2.balanceOf(bob), BOB_DEPOSIT);
|
|
}
|
|
|
|
function testTransferBalanceRejectsCallsWhilePaused() public {
|
|
vm.prank(owner);
|
|
bankV2.pause();
|
|
|
|
vm.prank(alice);
|
|
vm.expectRevert(PausableUpgradeable.EnforcedPause.selector);
|
|
bankV2.transferBalance(bob, 1);
|
|
|
|
assertEq(bankV2.balanceOf(alice), ALICE_DEPOSIT);
|
|
assertEq(bankV2.balanceOf(bob), BOB_DEPOSIT);
|
|
}
|
|
|
|
function testTransferBalanceCreditsRecipientWithNoPreviousBalance() public {
|
|
assertEq(bankV2.balanceOf(stranger), 0);
|
|
|
|
vm.prank(alice);
|
|
bankV2.transferBalance(stranger, 125e6);
|
|
|
|
assertEq(bankV2.balanceOf(alice), 875e6);
|
|
assertEq(bankV2.balanceOf(stranger), 125e6);
|
|
}
|
|
|
|
function testFuzzTransferBalanceAcrossTrackedRecipientsAndSenderBoundedAmounts(
|
|
uint256 recipientSeed,
|
|
uint256 amountSeed
|
|
) public {
|
|
address recipient = _trackedRecipient(bound(recipientSeed, 0, 2));
|
|
uint256 amount = bound(amountSeed, 1, ALICE_DEPOSIT);
|
|
uint256 recipientBefore = bankV2.balanceOf(recipient);
|
|
uint256 liabilitiesBefore = bankV2.totalLiabilities();
|
|
uint256 reservesBefore = token.balanceOf(proxy);
|
|
|
|
vm.prank(alice);
|
|
bankV2.transferBalance(recipient, amount);
|
|
|
|
assertEq(bankV2.balanceOf(alice), ALICE_DEPOSIT - amount);
|
|
assertEq(bankV2.balanceOf(recipient), recipientBefore + amount);
|
|
assertEq(bankV2.totalLiabilities(), liabilitiesBefore);
|
|
assertEq(token.balanceOf(proxy), reservesBefore);
|
|
}
|
|
|
|
function testV1DepositWithdrawalViewsPauseAndUnpauseStillWorkThroughV2Proxy() public {
|
|
_mintAndApprove(stranger, 100e6);
|
|
vm.prank(stranger);
|
|
bankV2.deposit(100e6);
|
|
|
|
vm.prank(alice);
|
|
bankV2.withdraw(100e6);
|
|
|
|
assertEq(address(bankV2.asset()), address(token));
|
|
assertEq(bankV2.owner(), owner);
|
|
assertEq(bankV2.balanceOf(alice), 900e6);
|
|
assertEq(bankV2.balanceOf(bob), BOB_DEPOSIT);
|
|
assertEq(bankV2.balanceOf(stranger), 100e6);
|
|
assertEq(bankV2.totalLiabilities(), 1_500e6);
|
|
assertEq(token.balanceOf(proxy), 1_500e6);
|
|
assertEq(bankV2.contractVersion(), 2);
|
|
|
|
vm.prank(owner);
|
|
bankV2.pause();
|
|
assertTrue(bankV2.paused());
|
|
|
|
vm.prank(owner);
|
|
bankV2.unpause();
|
|
assertFalse(bankV2.paused());
|
|
}
|
|
|
|
function _deposit(address account, uint256 amount) private {
|
|
_mintAndApprove(account, amount);
|
|
vm.prank(account);
|
|
bank.deposit(amount);
|
|
}
|
|
|
|
function _mintAndApprove(address account, uint256 amount) private {
|
|
vm.prank(owner);
|
|
token.mint(account, amount);
|
|
vm.prank(account);
|
|
token.approve(proxy, amount);
|
|
}
|
|
|
|
function _trackedRecipient(uint256 index) private view returns (address) {
|
|
if (index == 0) return bob;
|
|
if (index == 1) return stranger;
|
|
return owner;
|
|
}
|
|
}
|