Skip to content

Commit 92b6c92

Browse files
authored
feat: add test and rename error arg (#62)
1 parent 621f125 commit 92b6c92

File tree

3 files changed

+108
-7
lines changed

3 files changed

+108
-7
lines changed

src/XanV1.sol

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ contract XanV1 is
5555
bytes32 internal constant _XAN_V1_STORAGE_LOCATION =
5656
0x52f7d5fb153315ca313a5634db151fa7e0b41cd83fe6719e93ed3cd02b69d200;
5757

58-
error UnlockedBalanceInsufficient(address sender, uint256 unlockedBalance, uint256 valueToLock);
58+
error UnlockedBalanceInsufficient(address sender, uint256 unlockedBalance, uint256 requested);
5959
error LockedBalanceInsufficient(address sender, uint256 lockedBalance);
6060

6161
error ImplementationZero();
@@ -370,11 +370,7 @@ contract XanV1 is
370370
uint256 unlockedBalance = unlockedBalanceOf(from);
371371

372372
if (value > unlockedBalance) {
373-
revert UnlockedBalanceInsufficient({ // force linebreak
374-
sender: from,
375-
unlockedBalance: unlockedBalance,
376-
valueToLock: value
377-
});
373+
revert UnlockedBalanceInsufficient({sender: from, unlockedBalance: unlockedBalance, requested: value});
378374
}
379375
}
380376

@@ -389,7 +385,7 @@ contract XanV1 is
389385

390386
uint256 unlockedBalance = unlockedBalanceOf(account);
391387
if (value > unlockedBalance) {
392-
revert UnlockedBalanceInsufficient({sender: account, unlockedBalance: unlockedBalance, valueToLock: value});
388+
revert UnlockedBalanceInsufficient({sender: account, unlockedBalance: unlockedBalance, requested: value});
393389
}
394390

395391
data.lockedSupply += value;

test/XanV1.erc20.t.sol

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// SPDX-License-Identifier: AGPL-3.0-or-later
2+
pragma solidity ^0.8.30;
3+
4+
import {Upgrades, UnsafeUpgrades} from "@openzeppelin/foundry-upgrades/Upgrades.sol";
5+
import {Test} from "forge-std/Test.sol";
6+
7+
import {MockXanV1, XanV1} from "./mocks/XanV1.m.sol";
8+
9+
contract MockXanV1ERC20Test is Test {
10+
using UnsafeUpgrades for address;
11+
12+
address internal constant _COUNCIL = address(uint160(1));
13+
14+
address internal _alice;
15+
address internal _bob;
16+
MockXanV1 internal _xanProxyMock;
17+
18+
function setUp() public {
19+
(, _alice,) = vm.readCallers();
20+
21+
// Deploy proxy and mint tokens for the `_tokenHolder`.
22+
vm.prank(_alice);
23+
_xanProxyMock = MockXanV1(
24+
Upgrades.deployUUPSProxy({
25+
contractName: "XanV1.m.sol:MockXanV1",
26+
initializerData: abi.encodeCall(XanV1.initializeV1, (_alice, _COUNCIL))
27+
})
28+
);
29+
30+
_bob = vm.randomAddress();
31+
}
32+
33+
function test_update_MINT_does_not_require_address_0_to_hold_unlocked_tokens() public {
34+
address from = address(0);
35+
address to = _bob;
36+
uint256 value = 100;
37+
38+
assertEq(_xanProxyMock.balanceOf(from), 0);
39+
assertEq(_xanProxyMock.unlockedBalanceOf(from), 0);
40+
uint256 toBalanceBeforeUpdate = _xanProxyMock.unlockedBalanceOf(to);
41+
42+
_xanProxyMock.update({from: from, to: to, value: value});
43+
44+
assertEq(_xanProxyMock.balanceOf(from), 0);
45+
assertEq(_xanProxyMock.unlockedBalanceOf(from), 0);
46+
assertEq(_xanProxyMock.balanceOf(to), toBalanceBeforeUpdate + value);
47+
}
48+
49+
function test_update_TRANSFER_reverts_transfer_if_the_from_address_has_insufficient_unlocked_tokens() public {
50+
address from = _bob;
51+
address to = _alice;
52+
uint256 value = 100;
53+
54+
assertEq(_xanProxyMock.balanceOf(from), 0);
55+
assertEq(_xanProxyMock.unlockedBalanceOf(from), 0);
56+
57+
vm.expectRevert(
58+
abi.encodeWithSelector(XanV1.UnlockedBalanceInsufficient.selector, from, 0, value), address(_xanProxyMock)
59+
);
60+
_xanProxyMock.update({from: from, to: to, value: value});
61+
}
62+
63+
function test_update_TRANSFER_updates_if_the_from_address_has_sufficient_unlocked_tokens() public {
64+
address from = _alice;
65+
address to = _bob;
66+
uint256 value = 100;
67+
68+
uint256 fromBalanceBeforeUpdate = _xanProxyMock.unlockedBalanceOf(from);
69+
uint256 toBalanceBeforeUpdate = _xanProxyMock.unlockedBalanceOf(to);
70+
71+
_xanProxyMock.update({from: from, to: to, value: value});
72+
73+
assertEq(_xanProxyMock.balanceOf(from), fromBalanceBeforeUpdate - value);
74+
assertEq(_xanProxyMock.balanceOf(to), toBalanceBeforeUpdate + value);
75+
}
76+
77+
function test_update_BURN_reverts_if_the_from_address_has_unsufficient_unlocked_tokens() public {
78+
address from = _bob;
79+
address to = address(0);
80+
uint256 value = 100;
81+
82+
vm.expectRevert(
83+
abi.encodeWithSelector(XanV1.UnlockedBalanceInsufficient.selector, from, 0, value), address(_xanProxyMock)
84+
);
85+
_xanProxyMock.update({from: from, to: to, value: value});
86+
}
87+
88+
function test_update_BURN_updates_if_the_from_address_has_sufficient_unlocked_tokens() public {
89+
address from = _alice;
90+
address to = address(0);
91+
uint256 value = 100;
92+
93+
uint256 fromBalanceBeforeUpdate = _xanProxyMock.unlockedBalanceOf(from);
94+
assertEq(_xanProxyMock.unlockedBalanceOf(to), 0);
95+
96+
_xanProxyMock.update({from: from, to: to, value: value});
97+
98+
assertEq(_xanProxyMock.balanceOf(from), fromBalanceBeforeUpdate - value);
99+
assertEq(_xanProxyMock.balanceOf(to), 0);
100+
}
101+
}

test/mocks/XanV1.m.sol

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import {XanV1} from "../../src/XanV1.sol";
66
/// @notice This mock makes internal functions of the XanV1 token accessible to external callers.
77
/// @custom:oz-upgrades-unsafe-allow missing-initializer
88
contract MockXanV1 is XanV1 {
9+
function update(address from, address to, uint256 value) external {
10+
XanV1._update({from: from, to: to, value: value});
11+
}
12+
913
function isQuorumAndMinLockedSupplyReached(address impl) external view returns (bool isReached) {
1014
isReached = _isQuorumAndMinLockedSupplyReached(impl);
1115
}

0 commit comments

Comments
 (0)