Skip to content

Commit 900d4f9

Browse files
authored
chore: Add MINTER-ROLE and list to TST (#29)
* Add Approver list of accounts that can mint token * Update Approver to Minter role * Renaming of mapping and add events for minter add/remove * Formatting fix
1 parent 969d3ee commit 900d4f9

3 files changed

Lines changed: 203 additions & 34 deletions

File tree

test/TestStableToken.sol

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,36 @@ import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
66
import { ERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
77
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
88

9+
error AccountNotMinter();
10+
error AccountAlreadyMinter();
11+
error AccountNotInMinterList();
12+
913
contract TestStableToken is ERC20, ERC20Permit, Ownable {
14+
mapping(address => bool) public isMinter;
15+
16+
event MinterAdded(address indexed account);
17+
event MinterRemoved(address indexed account);
18+
19+
modifier onlyOwnerOrMinter() {
20+
if (msg.sender != owner() && !isMinter[msg.sender]) revert AccountNotMinter();
21+
_;
22+
}
23+
1024
constructor() ERC20("TestStableToken", "TST") ERC20Permit("TestStableToken") Ownable() { }
1125

12-
function mint(address to, uint256 amount) external onlyOwner {
26+
function addMinter(address account) external onlyOwner {
27+
if (isMinter[account]) revert AccountAlreadyMinter();
28+
isMinter[account] = true;
29+
emit MinterAdded(account);
30+
}
31+
32+
function removeMinter(address account) external onlyOwner {
33+
if (!isMinter[account]) revert AccountNotInMinterList();
34+
isMinter[account] = false;
35+
emit MinterRemoved(account);
36+
}
37+
38+
function mint(address to, uint256 amount) external onlyOwnerOrMinter {
1339
_mint(to, amount);
1440
}
1541
}

test/TestStableToken.t.sol

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity >=0.8.19 <0.9.0;
3+
4+
import { Test } from "forge-std/Test.sol";
5+
import { TestStableToken, AccountNotMinter, AccountAlreadyMinter, AccountNotInMinterList } from "./TestStableToken.sol";
6+
7+
contract TestStableTokenTest is Test {
8+
TestStableToken internal token;
9+
address internal owner;
10+
address internal user1;
11+
address internal user2;
12+
address internal nonMinter;
13+
14+
function setUp() public {
15+
token = new TestStableToken();
16+
owner = address(this);
17+
user1 = vm.addr(1);
18+
user2 = vm.addr(2);
19+
nonMinter = vm.addr(3);
20+
}
21+
22+
function test__OwnerCanAddMinterRole() external {
23+
assertFalse(token.isMinter(user1));
24+
25+
token.addMinter(user1);
26+
27+
assertTrue(token.isMinter(user1));
28+
}
29+
30+
function test__OwnerCanRemoveMinterRole() external {
31+
token.addMinter(user1);
32+
assertTrue(token.isMinter(user1));
33+
34+
token.removeMinter(user1);
35+
36+
assertFalse(token.isMinter(user1));
37+
}
38+
39+
function test__OwnerCanMintWithoutMinterRole() external {
40+
uint256 mintAmount = 1000 ether;
41+
42+
token.mint(user1, mintAmount);
43+
44+
assertEq(token.balanceOf(user1), mintAmount);
45+
}
46+
47+
function test__NonOwnerCannotAddMinterRole() external {
48+
vm.prank(user1);
49+
vm.expectRevert("Ownable: caller is not the owner");
50+
token.addMinter(user1);
51+
}
52+
53+
function test__NonOwnerCannotRemoveMinterRole() external {
54+
token.addMinter(user1);
55+
56+
vm.prank(user1);
57+
vm.expectRevert("Ownable: caller is not the owner");
58+
token.removeMinter(user1);
59+
}
60+
61+
function test__CannotAddAlreadyMinterRole() external {
62+
token.addMinter(user1);
63+
64+
vm.expectRevert(abi.encodeWithSelector(AccountAlreadyMinter.selector));
65+
token.addMinter(user1);
66+
}
67+
68+
function test__CannotRemoveNonMinterRole() external {
69+
vm.expectRevert(abi.encodeWithSelector(AccountNotInMinterList.selector));
70+
token.removeMinter(user1);
71+
}
72+
73+
function test__MinterRoleCanMint() external {
74+
uint256 mintAmount = 1000 ether;
75+
token.addMinter(user1);
76+
77+
vm.prank(user1);
78+
token.mint(user2, mintAmount);
79+
80+
assertEq(token.balanceOf(user2), mintAmount);
81+
}
82+
83+
function test__NonMinterNonOwnerAccountCannotMint() external {
84+
uint256 mintAmount = 1000 ether;
85+
86+
vm.prank(nonMinter);
87+
vm.expectRevert(abi.encodeWithSelector(AccountNotMinter.selector));
88+
token.mint(user1, mintAmount);
89+
}
90+
91+
function test__MultipleMinterRolesCanMint() external {
92+
uint256 mintAmount = 500 ether;
93+
token.addMinter(user1);
94+
token.addMinter(user2);
95+
96+
vm.prank(user1);
97+
token.mint(owner, mintAmount);
98+
99+
vm.prank(user2);
100+
token.mint(owner, mintAmount);
101+
102+
assertEq(token.balanceOf(owner), mintAmount * 2);
103+
}
104+
105+
function test__RemovedMinterRoleCannotMint() external {
106+
uint256 mintAmount = 1000 ether;
107+
token.addMinter(user1);
108+
token.removeMinter(user1);
109+
110+
vm.prank(user1);
111+
vm.expectRevert(abi.encodeWithSelector(AccountNotMinter.selector));
112+
token.mint(user2, mintAmount);
113+
}
114+
115+
function test__OwnerCanAlwaysMintEvenWithoutMinterRole() external {
116+
uint256 mintAmount = 500 ether;
117+
118+
// Owner is not in minter role but should still be able to mint
119+
assertFalse(token.isMinter(address(this)));
120+
token.mint(user1, mintAmount);
121+
assertEq(token.balanceOf(user1), mintAmount);
122+
}
123+
124+
function test__CheckMinterRoleMapping() external {
125+
assertFalse(token.isMinter(user1));
126+
assertFalse(token.isMinter(user2));
127+
128+
token.addMinter(user1);
129+
assertTrue(token.isMinter(user1));
130+
assertFalse(token.isMinter(user2));
131+
132+
token.addMinter(user2);
133+
assertTrue(token.isMinter(user1));
134+
assertTrue(token.isMinter(user2));
135+
136+
token.removeMinter(user1);
137+
assertFalse(token.isMinter(user1));
138+
assertTrue(token.isMinter(user2));
139+
}
140+
141+
function test__ERC20BasicFunctionality() external {
142+
token.addMinter(user1);
143+
uint256 mintAmount = 1000 ether;
144+
145+
vm.prank(user1);
146+
token.mint(user2, mintAmount);
147+
148+
assertEq(token.balanceOf(user2), mintAmount);
149+
assertEq(token.totalSupply(), mintAmount);
150+
151+
vm.prank(user2);
152+
token.transfer(owner, 200 ether);
153+
154+
assertEq(token.balanceOf(user2), 800 ether);
155+
assertEq(token.balanceOf(owner), 200 ether);
156+
}
157+
158+
function test__MinterAddedEventEmitted() external {
159+
vm.expectEmit(true, true, false, false);
160+
emit MinterAdded(user1);
161+
162+
token.addMinter(user1);
163+
}
164+
165+
function test__MinterRemovedEventEmitted() external {
166+
token.addMinter(user1);
167+
168+
vm.expectEmit(true, true, false, false);
169+
emit MinterRemoved(user1);
170+
171+
token.removeMinter(user1);
172+
}
173+
174+
event MinterAdded(address indexed account);
175+
event MinterRemoved(address indexed account);
176+
}

test/WakuRlnV2.t.sol

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -763,39 +763,6 @@ contract WakuRlnV2Test is Test {
763763
}
764764
}
765765

766-
function test__TestStableToken__OnlyOwnerCanMint() external {
767-
address nonOwner = vm.addr(1);
768-
uint256 mintAmount = 1000 ether;
769-
770-
vm.prank(nonOwner);
771-
vm.expectRevert("Ownable: caller is not the owner");
772-
token.mint(nonOwner, mintAmount);
773-
}
774-
775-
function test__TestStableToken__OwnerMintsTransfersAndRegisters() external {
776-
address recipient = vm.addr(2);
777-
uint256 idCommitment = 3;
778-
uint32 membershipRateLimit = w.minMembershipRateLimit();
779-
(, uint256 price) = w.priceCalculator().calculate(membershipRateLimit);
780-
781-
// Owner (test contract) mints tokens to recipient
782-
token.mint(recipient, price);
783-
assertEq(token.balanceOf(recipient), price);
784-
785-
// Recipient uses tokens to register
786-
vm.startPrank(recipient);
787-
token.approve(address(w), price);
788-
w.register(idCommitment, membershipRateLimit, noIdCommitmentsToErase);
789-
vm.stopPrank();
790-
791-
// Verify registration succeeded
792-
assertTrue(w.isInMembershipSet(idCommitment));
793-
(,,,, uint32 fetchedMembershipRateLimit, uint32 index, address holder,) = w.memberships(idCommitment);
794-
assertEq(fetchedMembershipRateLimit, membershipRateLimit);
795-
assertEq(holder, recipient);
796-
assertEq(index, 0);
797-
}
798-
799766
function test__Upgrade() external {
800767
address testImpl = address(new WakuRlnV2());
801768
bytes memory data = abi.encodeCall(WakuRlnV2.initialize, (address(0), 100, 1, 10, 10 minutes, 4 minutes));

0 commit comments

Comments
 (0)