Skip to content

Commit

Permalink
feat: adding btt tests for bToken (#167)
Browse files Browse the repository at this point in the history
* feat: adding btt tests for bToken

* feat: testing address zero checks on increase/decrease approvals

* feat: adding more checks to bToken tests
  • Loading branch information
wei3erHase authored Jul 23, 2024
1 parent c9a3849 commit eef9f2a
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 124 deletions.
217 changes: 93 additions & 124 deletions test/unit/BToken.t.sol
Original file line number Diff line number Diff line change
@@ -1,154 +1,123 @@
// SPDX-License-Identifier: GPL-3
pragma solidity ^0.8.25;
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;

import {IERC20} from '@openzeppelin/contracts/interfaces/IERC20.sol';
import {IERC20Errors} from '@openzeppelin/contracts/interfaces/draft-IERC6093.sol';
import {Test} from 'forge-std/Test.sol';
import {MockBToken} from 'test/smock/MockBToken.sol';

contract BToken_Unit_Constructor is Test {
function test_ConstructorParams() public {
MockBToken btoken = new MockBToken();
assertEq(btoken.name(), 'Balancer Pool Token');
assertEq(btoken.symbol(), 'BPT');
assertEq(btoken.decimals(), 18);
contract BToken is Test {
MockBToken public bToken;
uint256 public initialApproval = 100e18;
uint256 public initialBalance = 100e18;
address public caller = makeAddr('caller');
address public spender = makeAddr('spender');
address public target = makeAddr('target');

function setUp() external {
bToken = new MockBToken();

vm.startPrank(caller);
// sets initial approval (cannot be mocked)
bToken.approve(spender, initialApproval);
}
}

abstract contract BToken_Unit_base is Test {
MockBToken internal bToken;
function test_ConstructorWhenCalled() external {
MockBToken _bToken = new MockBToken();
// it sets token name
assertEq(_bToken.name(), 'Balancer Pool Token');
// it sets token symbol
assertEq(_bToken.symbol(), 'BPT');
}

modifier assumeNonZeroAddresses(address addr1, address addr2) {
vm.assume(addr1 != address(0));
vm.assume(addr2 != address(0));
_;
function test_IncreaseApprovalRevertWhen_SenderIsAddressZero() external {
vm.startPrank(address(0));
// it should revert
vm.expectRevert(abi.encodeWithSelector(IERC20Errors.ERC20InvalidApprover.selector, address(0)));

bToken.increaseApproval(spender, 100e18);
}

modifier assumeNonZeroAddress(address addr) {
vm.assume(addr != address(0));
_;
function test_IncreaseApprovalRevertWhen_SpenderIsAddressZero() external {
// it should revert
vm.expectRevert(abi.encodeWithSelector(IERC20Errors.ERC20InvalidSpender.selector, address(0)));
bToken.increaseApproval(address(0), 100e18);
}

function setUp() public virtual {
bToken = new MockBToken();
function test_IncreaseApprovalWhenCalled() external {
// it emits Approval event
vm.expectEmit();
emit IERC20.Approval(caller, spender, 200e18);

bToken.increaseApproval(spender, 100e18);
// it increases spender approval
assertEq(bToken.allowance(caller, spender), 200e18);
}
}

contract BToken_Unit_IncreaseApproval is BToken_Unit_base {
function test_increasesApprovalFromZero(
address sender,
address spender,
uint256 amount
) public assumeNonZeroAddresses(sender, spender) {
vm.prank(sender);
bToken.increaseApproval(spender, amount);
assertEq(bToken.allowance(sender, spender), amount);
function test_DecreaseApprovalRevertWhen_SenderIsAddressZero() external {
vm.startPrank(address(0));
// it should revert
vm.expectRevert(abi.encodeWithSelector(IERC20Errors.ERC20InvalidApprover.selector, address(0)));
bToken.decreaseApproval(spender, 50e18);
}

function test_increasesApprovalFromNonZero(
address sender,
address spender,
uint128 existingAllowance,
uint128 amount
) public assumeNonZeroAddresses(sender, spender) {
vm.assume(existingAllowance > 0);
vm.startPrank(sender);
bToken.approve(spender, existingAllowance);
bToken.increaseApproval(spender, amount);
vm.stopPrank();
assertEq(bToken.allowance(sender, spender), uint256(amount) + existingAllowance);
function test_DecreaseApprovalRevertWhen_SpenderIsAddressZero() external {
// it should revert
vm.expectRevert(abi.encodeWithSelector(IERC20Errors.ERC20InvalidSpender.selector, address(0)));
bToken.decreaseApproval(address(0), 50e18);
}
}

contract BToken_Unit_DecreaseApproval is BToken_Unit_base {
function test_decreaseApprovalToNonZero(
address sender,
address spender,
uint256 existingAllowance,
uint256 amount
) public assumeNonZeroAddresses(sender, spender) {
existingAllowance = bound(existingAllowance, 1, type(uint256).max);
amount = bound(amount, 0, existingAllowance - 1);
vm.startPrank(sender);
bToken.approve(spender, existingAllowance);
bToken.decreaseApproval(spender, amount);
vm.stopPrank();
assertEq(bToken.allowance(sender, spender), existingAllowance - amount);
function test_DecreaseApprovalWhenDecrementIsBiggerThanCurrentApproval() external {
bToken.decreaseApproval(spender, 200e18);
// it decreases spender approval to 0
assertEq(bToken.allowance(caller, spender), 0);
}

function test_decreaseApprovalToZero(
address sender,
address spender,
uint256 existingAllowance,
uint256 amount
) public assumeNonZeroAddresses(sender, spender) {
amount = bound(amount, existingAllowance, type(uint256).max);
vm.startPrank(sender);
bToken.approve(spender, existingAllowance);
bToken.decreaseApproval(spender, amount);
vm.stopPrank();
assertEq(bToken.allowance(sender, spender), 0);
function test_DecreaseApprovalWhenCalled() external {
// it emits Approval event
vm.expectEmit();
emit IERC20.Approval(caller, spender, 50e18);

bToken.decreaseApproval(spender, 50e18);
// it decreases spender approval
assertEq(bToken.allowance(caller, spender), 50e18);
}
}

contract BToken_Unit__push is BToken_Unit_base {
function test_revertsOnInsufficientSelfBalance(
address to,
uint128 existingBalance,
uint128 offset
) public assumeNonZeroAddress(to) {
vm.assume(offset > 1);
deal(address(bToken), address(bToken), existingBalance);
vm.expectRevert(
abi.encodeWithSelector(
IERC20Errors.ERC20InsufficientBalance.selector,
address(bToken),
existingBalance,
uint256(existingBalance) + offset
)
);
bToken.call__push(to, uint256(existingBalance) + offset);
function test__pushRevertWhen_ContractDoesNotHaveEnoughBalance() external {
// it should revert
vm.expectRevert(abi.encodeWithSelector(IERC20Errors.ERC20InsufficientBalance.selector, address(bToken), 0, 50e18));
bToken.call__push(target, 50e18);
}

function test_sendsTokens(
address to,
uint128 existingBalance,
uint256 transferAmount
) public assumeNonZeroAddress(to) {
vm.assume(to != address(bToken));
transferAmount = bound(transferAmount, 0, existingBalance);
deal(address(bToken), address(bToken), existingBalance);
bToken.call__push(to, transferAmount);
assertEq(bToken.balanceOf(to), transferAmount);
assertEq(bToken.balanceOf(address(bToken)), existingBalance - transferAmount);
function test__pushWhenCalled() external {
deal(address(bToken), address(bToken), initialBalance);
// it emits Transfer event
vm.expectEmit();
emit IERC20.Transfer(address(bToken), target, 50e18);

bToken.call__push(target, 50e18);

// it transfers tokens to recipient
assertEq(bToken.balanceOf(address(bToken)), 50e18);
assertEq(bToken.balanceOf(target), 50e18);
}
}

contract BToken_Unit__pull is BToken_Unit_base {
function test_revertsOnInsufficientFromBalance(
address from,
uint128 existingBalance,
uint128 offset
) public assumeNonZeroAddress(from) {
vm.assume(offset > 1);
deal(address(bToken), from, existingBalance);
vm.expectRevert(
abi.encodeWithSelector(
IERC20Errors.ERC20InsufficientBalance.selector, from, existingBalance, uint256(existingBalance) + offset
)
);
bToken.call__pull(from, uint256(existingBalance) + offset);
function test__pullRevertWhen_TargetDoesNotHaveEnoughBalance() external {
// it should revert
vm.expectRevert(abi.encodeWithSelector(IERC20Errors.ERC20InsufficientBalance.selector, target, 0, 50e18));
bToken.call__pull(target, 50e18);
}

function test_getsTokens(
address from,
uint128 existingBalance,
uint256 transferAmount
) public assumeNonZeroAddress(from) {
vm.assume(from != address(bToken));
transferAmount = bound(transferAmount, 0, existingBalance);
deal(address(bToken), address(from), existingBalance);
bToken.call__pull(from, transferAmount);
assertEq(bToken.balanceOf(address(bToken)), transferAmount);
assertEq(bToken.balanceOf(from), existingBalance - transferAmount);
function test__pullWhenCalled() external {
deal(address(bToken), address(target), initialBalance);
// it emits Transfer event
vm.expectEmit();
emit IERC20.Transfer(target, address(bToken), 50e18);

bToken.call__pull(target, 50e18);

// it transfers tokens from sender
assertEq(bToken.balanceOf(target), 50e18);
assertEq(bToken.balanceOf(address(bToken)), 50e18);
}
}
38 changes: 38 additions & 0 deletions test/unit/BToken.tree
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
BToken::constructor
└── when called
├── it sets token name
└── it sets token symbol

BToken::increaseApproval
├── when sender is address zero
│ └── it should revert
├── when spender is address zero
│ └── it should revert
└── when called
├── it emits Approval event
└── it increases spender approval

BToken::decreaseApproval
├── when sender is address zero
│ └── it should revert
├── when spender is address zero
│ └── it should revert
├── when decrement is bigger than current approval
│ └── it decreases spender approval to 0
└── when called
├── it emits Approval event
└── it decreases spender approval

BToken::_push
├── when contract does not have enough balance
│ └── it should revert
└── when called
├── it emits Transfer event
└── it transfers tokens to recipient

BToken::_pull
├── when target does not have enough balance
│ └── it should revert
└── when called
├── it emits Transfer event
└── it transfers tokens from sender

0 comments on commit eef9f2a

Please sign in to comment.