Skip to content

Commit c7c64b7

Browse files
authored
Merge pull request #260 from lumoswiz/test/erc20-transfer-facet
Align ERC-20 returns and complete ERC20TransferFacet fuzz unit tests
2 parents dcc2c99 + 78df956 commit c7c64b7

17 files changed

Lines changed: 456 additions & 108 deletions

File tree

src/token/ERC20/ERC20/ERC20Mod.sol

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,9 @@ function burn(address _account, uint256 _value) {
132132
* @param _from The address to send tokens from.
133133
* @param _to The address to send tokens to.
134134
* @param _value The number of tokens to transfer.
135+
* @return True if the transfer was successful.
135136
*/
136-
function transferFrom(address _from, address _to, uint256 _value) {
137+
function transferFrom(address _from, address _to, uint256 _value) returns (bool) {
137138
ERC20TransferStorage storage s = getStorage();
138139
if (_from == address(0)) {
139140
revert ERC20InvalidSender(address(0));
@@ -157,15 +158,17 @@ function transferFrom(address _from, address _to, uint256 _value) {
157158
}
158159
s.balanceOf[_to] += _value;
159160
emit Transfer(_from, _to, _value);
161+
return true;
160162
}
161163

162164
/**
163165
* @notice Transfers tokens from the caller to another address.
164166
* @dev Updates balances directly without allowance mechanism.
165167
* @param _to The address to send tokens to.
166168
* @param _value The number of tokens to transfer.
169+
* @return True if the transfer was successful.
167170
*/
168-
function transfer(address _to, uint256 _value) {
171+
function transfer(address _to, uint256 _value) returns (bool) {
169172
ERC20TransferStorage storage s = getStorage();
170173
if (_to == address(0)) {
171174
revert ERC20InvalidReceiver(address(0));
@@ -179,19 +182,22 @@ function transfer(address _to, uint256 _value) {
179182
}
180183
s.balanceOf[_to] += _value;
181184
emit Transfer(msg.sender, _to, _value);
185+
return true;
182186
}
183187

184188
/**
185189
* @notice Approves a spender to transfer tokens on behalf of the caller.
186190
* @dev Sets the allowance for the spender.
187191
* @param _spender The address to approve for spending.
188192
* @param _value The amount of tokens to approve.
193+
* @return True if the approval was successful.
189194
*/
190-
function approve(address _spender, uint256 _value) {
195+
function approve(address _spender, uint256 _value) returns (bool) {
191196
if (_spender == address(0)) {
192197
revert ERC20InvalidSpender(address(0));
193198
}
194199
ERC20TransferStorage storage s = getStorage();
195200
s.allowance[msg.sender][_spender] = _value;
196201
emit Approval(msg.sender, _spender, _value);
202+
return true;
197203
}

test/harnesses/token/ERC20/ERC20/ERC20Harness.sol

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,22 +30,22 @@ contract ERC20Harness {
3030
/**
3131
* @notice Exposes ERC20Mod.transferFrom as an external function
3232
*/
33-
function transferFrom(address _from, address _to, uint256 _value) external {
34-
ERC20Mod.transferFrom(_from, _to, _value);
33+
function transferFrom(address _from, address _to, uint256 _value) external returns (bool) {
34+
return ERC20Mod.transferFrom(_from, _to, _value);
3535
}
3636

3737
/**
3838
* @notice Exposes ERC20Mod.transfer as an external function
3939
*/
40-
function transfer(address _to, uint256 _value) external {
41-
ERC20Mod.transfer(_to, _value);
40+
function transfer(address _to, uint256 _value) external returns (bool) {
41+
return ERC20Mod.transfer(_to, _value);
4242
}
4343

4444
/**
4545
* @notice Exposes ERC20Mod.approve as an external function
4646
*/
47-
function approve(address _spender, uint256 _value) external {
48-
ERC20Mod.approve(_spender, _value);
47+
function approve(address _spender, uint256 _value) external returns (bool) {
48+
return ERC20Mod.approve(_spender, _value);
4949
}
5050

5151
function totalSupply() external view returns (uint256) {

test/trees/ERC20.tree

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
Approve
2+
├── when the spender is the zero address
3+
│ └── it should revert
4+
└── when the spender is not the zero address
5+
├── it should set the caller's allowance for the spender to value
6+
├── it should emit an {Approval} event
7+
└── it should return true
8+
9+
Transfer
10+
├── when the receiver is the zero address
11+
│ └── it should revert
12+
└── when the receiver is not the zero address
13+
├── given when the sender's balance is less than the transfer amount
14+
│ └── it should revert
15+
└── given when the sender's balance is greater than, or equal to, the transfer amount
16+
├── when the amount is zero
17+
│ ├── it should emit a {Transfer} event
18+
│ ├── it should not change balances
19+
│ └── it should return true
20+
└── when the amount is > 0
21+
├── it should decrement the sender's balance by the transfer amount
22+
├── it should increment the receiver's balance by the transfer amount
23+
├── it should emit a {Transfer} event
24+
└── it should return true
25+
26+
TransferFrom
27+
├── when the sender is the zero address
28+
│ └── it should revert
29+
├── when the receiver is the zero address
30+
│ └── it should revert
31+
└── when both sender and receiver are non-zero addresses
32+
├── given allowance < amount
33+
│ └── it should revert
34+
└── given allowance >= amount
35+
├── given balance < amount
36+
│ └── it should revert
37+
└── given balance >= amount
38+
├── when amount is zero
39+
│ ├── it should not change balances
40+
│ ├── it should not decrement allowance
41+
│ ├── it should emit a {Transfer} event
42+
│ └── it should return true
43+
├── when amount > 0 and allowance is max uint256
44+
│ ├── it should decrement the sender's balance by the transfer amount
45+
│ ├── it should increment the receiver's balance by the transfer amount
46+
│ ├── it should not change allowance
47+
│ ├── it should emit a {Transfer} event
48+
│ └── it should return true
49+
└── when the amount is greater than zero and allowance is finite
50+
├── it should decrement the allowance by the transfer amount
51+
├── it should decrement the sender's balance by the transfer amount
52+
├── it should increment the receiver's balance by the transfer amount
53+
├── it should emit a {Transfer} event
54+
└── it should return true
55+
56+
Mint
57+
├── when the account to mint to is the zero address
58+
│ └── it should revert
59+
└── when the account to mint to is not the zero address
60+
├── given the mint amount would overflow total supply
61+
│ └── it should revert
62+
└── given the mint amount does not overflow total supply
63+
├── it should increment total supply by the mint amount
64+
├── it should increment the account's balance by the mint amount
65+
└── it should emit a {Transfer} event from zero address to the account
66+
67+
Burn
68+
├── when the account to burn from is the zero address
69+
│ └── it should revert
70+
└── when the account to burn from is not the zero address
71+
├── when the account balance is less than the burn amount
72+
│ └── it should revert
73+
└── when the account balance is greater than or equal to the burn amount
74+
├── it should decrement the account balance by the burn amount
75+
├── it should decrement total supply by the burn amount
76+
└── it should emit a {Transfer} event to the zero address

test/unit/fuzz/token/ERC20/ERC20/mod/approve.tree

Lines changed: 0 additions & 6 deletions
This file was deleted.

test/unit/fuzz/token/ERC20/ERC20/mod/burn.tree

Lines changed: 0 additions & 10 deletions
This file was deleted.

test/unit/fuzz/token/ERC20/ERC20/mod/mint.tree

Lines changed: 0 additions & 10 deletions
This file was deleted.

test/unit/fuzz/token/ERC20/ERC20/mod/transfer.tree

Lines changed: 0 additions & 10 deletions
This file was deleted.

test/unit/fuzz/token/ERC20/ERC20/mod/transferFrom.tree

Lines changed: 0 additions & 22 deletions
This file was deleted.

test/unit/fuzz/token/ERC20/ERC20/mod/approve.t.sol renamed to test/unit/token/ERC20/ERC20/mod/fuzz/approve.t.sol

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,21 @@ import {stdError} from "forge-std/StdError.sol";
99
import {Base_Test} from "test/Base.t.sol";
1010
import {ERC20Harness} from "test/harnesses/token/ERC20/ERC20/ERC20Harness.sol";
1111

12-
import "src/token/ERC20/ERC20/ERC20Mod.sol" as ERC20Mod;
12+
import "src/token/ERC20/ERC20/ERC20Mod.sol";
1313

14+
/**
15+
* @dev BTT spec: test/trees/ERC20.tree
16+
*/
1417
contract Approve_ERC20Mod_Fuzz_Unit_Test is Base_Test {
1518
ERC20Harness internal harness;
1619

17-
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
18-
1920
function setUp() public override {
2021
Base_Test.setUp();
21-
2222
harness = new ERC20Harness();
2323
}
2424

2525
function testFuzz_ShouldRevert_SpenderIsZeroAddress(uint256 value) external {
26-
vm.expectRevert(abi.encodeWithSelector(ERC20Mod.ERC20InvalidSpender.selector, ADDRESS_ZERO));
26+
vm.expectRevert(abi.encodeWithSelector(ERC20InvalidSpender.selector, ADDRESS_ZERO));
2727
harness.approve(ADDRESS_ZERO, value);
2828
}
2929

@@ -32,9 +32,9 @@ contract Approve_ERC20Mod_Fuzz_Unit_Test is Base_Test {
3232

3333
vm.expectEmit(address(harness));
3434
emit Approval(users.alice, spender, value);
35-
harness.approve(spender, value);
35+
bool result = harness.approve(spender, value);
3636

37+
assertEq(result, true, "approve failed");
3738
assertEq(harness.allowance(users.alice, spender), value);
3839
}
3940
}
40-

test/unit/fuzz/token/ERC20/ERC20/mod/burn.t.sol renamed to test/unit/token/ERC20/ERC20/mod/fuzz/burn.t.sol

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,21 @@ import {stdError} from "forge-std/StdError.sol";
99
import {Base_Test} from "test/Base.t.sol";
1010
import {ERC20Harness} from "test/harnesses/token/ERC20/ERC20/ERC20Harness.sol";
1111

12-
import "src/token/ERC20/ERC20/ERC20Mod.sol" as ERC20Mod;
12+
import "src/token/ERC20/ERC20/ERC20Mod.sol";
1313

14+
/**
15+
* @dev BTT spec: test/trees/ERC20.tree
16+
*/
1417
contract Burn_ERC20Mod_Fuzz_Unit_Test is Base_Test {
1518
ERC20Harness internal harness;
1619

17-
event Transfer(address indexed _from, address indexed _to, uint256 _value);
18-
1920
function setUp() public override {
2021
Base_Test.setUp();
21-
2222
harness = new ERC20Harness();
2323
}
2424

2525
function testFuzz_ShouldRevert_Account_ZeroAddress(uint256 value) external {
26-
vm.expectRevert(abi.encodeWithSelector(ERC20Mod.ERC20InvalidSender.selector, address(0)));
26+
vm.expectRevert(abi.encodeWithSelector(ERC20InvalidSender.selector, address(0)));
2727
harness.burn(ADDRESS_ZERO, value);
2828
}
2929

@@ -37,7 +37,7 @@ contract Burn_ERC20Mod_Fuzz_Unit_Test is Base_Test {
3737

3838
harness.mint(account, balance);
3939

40-
vm.expectRevert(abi.encodeWithSelector(ERC20Mod.ERC20InsufficientBalance.selector, account, balance, value));
40+
vm.expectRevert(abi.encodeWithSelector(ERC20InsufficientBalance.selector, account, balance, value));
4141
harness.burn(account, value);
4242
}
4343

@@ -63,4 +63,3 @@ contract Burn_ERC20Mod_Fuzz_Unit_Test is Base_Test {
6363
assertEq(harness.balanceOf(account), beforeBalanceOfAccount - value, "balanceOf(account)");
6464
}
6565
}
66-

0 commit comments

Comments
 (0)