mirror of
https://github.com/logos-messaging/logos-messaging-rlnv2-contract.git
synced 2026-01-07 16:33:10 +00:00
chore: Add mint function that requires ETH to burn (#33)
* remove ownable to clear compiler error * Add mintWithEth function to TST to burn Eth * Update test/README.md with mintWithETH usage * remove unnecessary 'revert ETHTransferFailed' in mintWithETH * Move emit functions to top of TestStabletoken.t.sol script * Add max token supply mechanism for TST * Linting fix * Update max eth used in WakuRlnv2 test Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Use 1 to 1 eth burn per token ratio --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
parent
b4508dd0d4
commit
71191ce151
@ -15,7 +15,7 @@ contract DeployTokenWithProxy is BaseScript {
|
|||||||
address implementation = address(new TestStableToken());
|
address implementation = address(new TestStableToken());
|
||||||
|
|
||||||
// Encode the initialize call
|
// Encode the initialize call
|
||||||
bytes memory data = abi.encodeCall(TestStableToken.initialize, ());
|
bytes memory data = abi.encodeCall(TestStableToken.initialize, (1_000_000 * 10 ** 18));
|
||||||
|
|
||||||
// Deploy the proxy with initialization data
|
// Deploy the proxy with initialization data
|
||||||
return new ERC1967Proxy(implementation, data);
|
return new ERC1967Proxy(implementation, data);
|
||||||
|
|||||||
@ -15,7 +15,7 @@ contract LinearPriceCalculator is IPriceCalculator, Ownable {
|
|||||||
/// @notice The price per message per epoch
|
/// @notice The price per message per epoch
|
||||||
uint256 public pricePerMessagePerEpoch;
|
uint256 public pricePerMessagePerEpoch;
|
||||||
|
|
||||||
constructor(address _token, uint256 _pricePerMessagePerEpoch) Ownable() {
|
constructor(address _token, uint256 _pricePerMessagePerEpoch) {
|
||||||
_setTokenAndPrice(_token, _pricePerMessagePerEpoch);
|
_setTokenAndPrice(_token, _pricePerMessagePerEpoch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -60,10 +60,22 @@ cast send $TOKEN_PROXY_ADDRESS "addMinter(address)" $ACCOUNT_ADDRESS --rpc-url $
|
|||||||
|
|
||||||
### Mint tokens to the account
|
### Mint tokens to the account
|
||||||
|
|
||||||
|
#### Option 1: Restricted minting (requires minter privileges)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cast send $TOKEN_PROXY_ADDRESS "mint(address,uint256)" <TO_ADDRESS> <AMOUNT> --rpc-url $RPC_URL --private-key $MINTER_ACCOUNT_PRIVATE_KEY
|
cast send $TOKEN_PROXY_ADDRESS "mint(address,uint256)" <TO_ADDRESS> <AMOUNT> --rpc-url $RPC_URL --private-key $MINTER_ACCOUNT_PRIVATE_KEY
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Option 2: Public minting by burning ETH (no privileges required)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cast send $TOKEN_PROXY_ADDRESS "mintWithETH(address,uint256)" <TO_ACCOUNT> <AMOUNT> --value <ETH_AMOUNT> --rpc-url $RPC_URL --private-key $MINTING_ACCOUNT_PRIVATE_KEY --from $MINTING_ACCOUNT_ADDRESS
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: The `mintWithETH` function is public and can be called by anyone. It requires sending ETH with the transaction
|
||||||
|
(using `--value`), which gets burned (sent to address(0)) as an economic cost for minting tokens. This provides a
|
||||||
|
permissionless way to obtain tokens for testing without requiring minter privileges.
|
||||||
|
|
||||||
### Approve the token for the waku-rlnv2-contract to use
|
### Approve the token for the waku-rlnv2-contract to use
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@ -12,6 +12,8 @@ import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils
|
|||||||
error AccountNotMinter();
|
error AccountNotMinter();
|
||||||
error AccountAlreadyMinter();
|
error AccountAlreadyMinter();
|
||||||
error AccountNotInMinterList();
|
error AccountNotInMinterList();
|
||||||
|
error InsufficientETH();
|
||||||
|
error ExceedsMaxSupply();
|
||||||
|
|
||||||
contract TestStableToken is
|
contract TestStableToken is
|
||||||
Initializable,
|
Initializable,
|
||||||
@ -21,9 +23,12 @@ contract TestStableToken is
|
|||||||
UUPSUpgradeable
|
UUPSUpgradeable
|
||||||
{
|
{
|
||||||
mapping(address => bool) public isMinter;
|
mapping(address => bool) public isMinter;
|
||||||
|
uint256 public maxSupply;
|
||||||
|
|
||||||
event MinterAdded(address indexed account);
|
event MinterAdded(address indexed account);
|
||||||
event MinterRemoved(address indexed account);
|
event MinterRemoved(address indexed account);
|
||||||
|
event ETHBurned(uint256 amount, address indexed minter, address indexed to, uint256 tokensMinted);
|
||||||
|
event MaxSupplySet(uint256 oldMaxSupply, uint256 newMaxSupply);
|
||||||
|
|
||||||
modifier onlyOwnerOrMinter() {
|
modifier onlyOwnerOrMinter() {
|
||||||
if (msg.sender != owner() && !isMinter[msg.sender]) revert AccountNotMinter();
|
if (msg.sender != owner() && !isMinter[msg.sender]) revert AccountNotMinter();
|
||||||
@ -34,11 +39,13 @@ contract TestStableToken is
|
|||||||
_disableInitializers();
|
_disableInitializers();
|
||||||
}
|
}
|
||||||
|
|
||||||
function initialize() public initializer {
|
function initialize(uint256 _maxSupply) public initializer {
|
||||||
__ERC20_init("TestStableToken", "TST");
|
__ERC20_init("TestStableToken", "TST");
|
||||||
__ERC20Permit_init("TestStableToken");
|
__ERC20Permit_init("TestStableToken");
|
||||||
__Ownable_init();
|
__Ownable_init();
|
||||||
__UUPSUpgradeable_init();
|
__UUPSUpgradeable_init();
|
||||||
|
|
||||||
|
maxSupply = _maxSupply;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _authorizeUpgrade(address newImplementation) internal override onlyOwner { }
|
function _authorizeUpgrade(address newImplementation) internal override onlyOwner { }
|
||||||
@ -56,8 +63,30 @@ contract TestStableToken is
|
|||||||
}
|
}
|
||||||
|
|
||||||
function mint(address to, uint256 amount) external onlyOwnerOrMinter {
|
function mint(address to, uint256 amount) external onlyOwnerOrMinter {
|
||||||
|
if (totalSupply() + amount > maxSupply) revert ExceedsMaxSupply();
|
||||||
_mint(to, amount);
|
_mint(to, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mintWithETH(address to) external payable {
|
||||||
|
if (msg.value == 0) revert InsufficientETH();
|
||||||
|
if (totalSupply() + msg.value > maxSupply) revert ExceedsMaxSupply();
|
||||||
|
|
||||||
|
// Burn ETH by sending to zero address
|
||||||
|
payable(address(0)).transfer(msg.value);
|
||||||
|
|
||||||
|
_mint(to, msg.value);
|
||||||
|
|
||||||
|
emit ETHBurned(msg.value, msg.sender, to, msg.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMaxSupply(uint256 _maxSupply) external onlyOwner {
|
||||||
|
if (_maxSupply < totalSupply()) revert ExceedsMaxSupply();
|
||||||
|
|
||||||
|
uint256 oldMaxSupply = maxSupply;
|
||||||
|
maxSupply = _maxSupply;
|
||||||
|
|
||||||
|
emit MaxSupplySet(oldMaxSupply, _maxSupply);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contract TestStableTokenFactory is BaseScript {
|
contract TestStableTokenFactory is BaseScript {
|
||||||
|
|||||||
@ -2,7 +2,14 @@
|
|||||||
pragma solidity >=0.8.19 <0.9.0;
|
pragma solidity >=0.8.19 <0.9.0;
|
||||||
|
|
||||||
import { Test } from "forge-std/Test.sol";
|
import { Test } from "forge-std/Test.sol";
|
||||||
import { TestStableToken, AccountNotMinter, AccountAlreadyMinter, AccountNotInMinterList } from "./TestStableToken.sol";
|
import {
|
||||||
|
TestStableToken,
|
||||||
|
AccountNotMinter,
|
||||||
|
AccountAlreadyMinter,
|
||||||
|
AccountNotInMinterList,
|
||||||
|
InsufficientETH,
|
||||||
|
ExceedsMaxSupply
|
||||||
|
} from "./TestStableToken.sol";
|
||||||
import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
|
import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
|
||||||
import { DeployTokenWithProxy } from "../script/DeployTokenWithProxy.s.sol";
|
import { DeployTokenWithProxy } from "../script/DeployTokenWithProxy.s.sol";
|
||||||
|
|
||||||
@ -14,6 +21,11 @@ contract TestStableTokenTest is Test {
|
|||||||
address internal user2;
|
address internal user2;
|
||||||
address internal nonMinter;
|
address internal nonMinter;
|
||||||
|
|
||||||
|
event MinterAdded(address indexed account);
|
||||||
|
event MinterRemoved(address indexed account);
|
||||||
|
event ETHBurned(uint256 amount, address indexed minter, address indexed to, uint256 tokensMinted);
|
||||||
|
event MaxSupplySet(uint256 oldMaxSupply, uint256 newMaxSupply);
|
||||||
|
|
||||||
function setUp() public {
|
function setUp() public {
|
||||||
// Deploy using the deployment script
|
// Deploy using the deployment script
|
||||||
deployer = new DeployTokenWithProxy();
|
deployer = new DeployTokenWithProxy();
|
||||||
@ -51,6 +63,9 @@ contract TestStableTokenTest is Test {
|
|||||||
function test__OwnerCanMintWithoutMinterRole() external {
|
function test__OwnerCanMintWithoutMinterRole() external {
|
||||||
uint256 mintAmount = 1000 ether;
|
uint256 mintAmount = 1000 ether;
|
||||||
|
|
||||||
|
// Owner is not in minter role but should still be able to mint
|
||||||
|
assertFalse(token.isMinter(owner));
|
||||||
|
|
||||||
vm.prank(owner);
|
vm.prank(owner);
|
||||||
token.mint(user1, mintAmount);
|
token.mint(user1, mintAmount);
|
||||||
|
|
||||||
@ -134,16 +149,6 @@ contract TestStableTokenTest is Test {
|
|||||||
token.mint(user2, mintAmount);
|
token.mint(user2, mintAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
function test__OwnerCanAlwaysMintEvenWithoutMinterRole() external {
|
|
||||||
uint256 mintAmount = 500 ether;
|
|
||||||
|
|
||||||
// Owner is not in minter role but should still be able to mint
|
|
||||||
assertFalse(token.isMinter(owner));
|
|
||||||
vm.prank(owner);
|
|
||||||
token.mint(user1, mintAmount);
|
|
||||||
assertEq(token.balanceOf(user1), mintAmount);
|
|
||||||
}
|
|
||||||
|
|
||||||
function test__CheckMinterRoleMapping() external {
|
function test__CheckMinterRoleMapping() external {
|
||||||
assertFalse(token.isMinter(user1));
|
assertFalse(token.isMinter(user1));
|
||||||
assertFalse(token.isMinter(user2));
|
assertFalse(token.isMinter(user2));
|
||||||
@ -164,24 +169,6 @@ contract TestStableTokenTest is Test {
|
|||||||
assertTrue(token.isMinter(user2));
|
assertTrue(token.isMinter(user2));
|
||||||
}
|
}
|
||||||
|
|
||||||
function test__ERC20BasicFunctionality() external {
|
|
||||||
vm.prank(owner);
|
|
||||||
token.addMinter(user1);
|
|
||||||
uint256 mintAmount = 1000 ether;
|
|
||||||
|
|
||||||
vm.prank(user1);
|
|
||||||
token.mint(user2, mintAmount);
|
|
||||||
|
|
||||||
assertEq(token.balanceOf(user2), mintAmount);
|
|
||||||
assertEq(token.totalSupply(), mintAmount);
|
|
||||||
|
|
||||||
vm.prank(user2);
|
|
||||||
assertTrue(token.transfer(owner, 200 ether));
|
|
||||||
|
|
||||||
assertEq(token.balanceOf(user2), 800 ether);
|
|
||||||
assertEq(token.balanceOf(owner), 200 ether);
|
|
||||||
}
|
|
||||||
|
|
||||||
function test__MinterAddedEventEmitted() external {
|
function test__MinterAddedEventEmitted() external {
|
||||||
vm.expectEmit(true, true, false, false);
|
vm.expectEmit(true, true, false, false);
|
||||||
emit MinterAdded(user1);
|
emit MinterAdded(user1);
|
||||||
@ -201,6 +188,152 @@ contract TestStableTokenTest is Test {
|
|||||||
token.removeMinter(user1);
|
token.removeMinter(user1);
|
||||||
}
|
}
|
||||||
|
|
||||||
event MinterAdded(address indexed account);
|
function test__MintRequiresETH() external {
|
||||||
event MinterRemoved(address indexed account);
|
vm.prank(owner);
|
||||||
|
vm.expectRevert(abi.encodeWithSelector(InsufficientETH.selector));
|
||||||
|
token.mintWithETH(user1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test__ERC20BasicFunctionality() external {
|
||||||
|
uint256 ethAmount = 0.1 ether;
|
||||||
|
|
||||||
|
vm.deal(user1, ethAmount);
|
||||||
|
vm.prank(user1);
|
||||||
|
token.mintWithETH{ value: ethAmount }(user2);
|
||||||
|
|
||||||
|
assertEq(token.balanceOf(user2), ethAmount);
|
||||||
|
assertEq(token.totalSupply(), ethAmount);
|
||||||
|
|
||||||
|
vm.prank(user2);
|
||||||
|
assertTrue(token.transfer(owner, 0.05 ether));
|
||||||
|
|
||||||
|
assertEq(token.balanceOf(user2), 0.05 ether);
|
||||||
|
assertEq(token.balanceOf(owner), 0.05 ether);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test__ETHBurnedEventEmitted() external {
|
||||||
|
uint256 ethAmount = 0.1 ether;
|
||||||
|
|
||||||
|
vm.deal(owner, ethAmount);
|
||||||
|
|
||||||
|
vm.expectEmit(true, true, true, true);
|
||||||
|
emit ETHBurned(ethAmount, owner, user1, ethAmount);
|
||||||
|
|
||||||
|
vm.prank(owner);
|
||||||
|
token.mintWithETH{ value: ethAmount }(user1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test__ETHIsBurnedToZeroAddress() external {
|
||||||
|
uint256 ethAmount = 0.1 ether;
|
||||||
|
address zeroAddress = address(0);
|
||||||
|
|
||||||
|
uint256 zeroBalanceBefore = zeroAddress.balance;
|
||||||
|
|
||||||
|
vm.deal(owner, ethAmount);
|
||||||
|
vm.prank(owner);
|
||||||
|
token.mintWithETH{ value: ethAmount }(user1);
|
||||||
|
|
||||||
|
// ETH should be burned to zero address
|
||||||
|
assertEq(zeroAddress.balance, zeroBalanceBefore + ethAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test__ContractDoesNotHoldETHAfterMint() external {
|
||||||
|
uint256 ethAmount = 0.1 ether;
|
||||||
|
|
||||||
|
uint256 contractBalanceBefore = address(token).balance;
|
||||||
|
|
||||||
|
vm.deal(owner, ethAmount);
|
||||||
|
vm.prank(owner);
|
||||||
|
token.mintWithETH{ value: ethAmount }(user1);
|
||||||
|
|
||||||
|
// Contract should not hold any ETH after mint
|
||||||
|
assertEq(address(token).balance, contractBalanceBefore);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test__MintWithDifferentETHAmounts() external {
|
||||||
|
uint256[] memory ethAmounts = new uint256[](3);
|
||||||
|
ethAmounts[0] = 0.01 ether;
|
||||||
|
ethAmounts[1] = 1 ether;
|
||||||
|
ethAmounts[2] = 10 ether;
|
||||||
|
|
||||||
|
for (uint256 i = 0; i < ethAmounts.length; i++) {
|
||||||
|
address user = vm.addr(i + 10);
|
||||||
|
vm.deal(owner, ethAmounts[i]);
|
||||||
|
|
||||||
|
vm.expectEmit(true, true, true, true);
|
||||||
|
emit ETHBurned(ethAmounts[i], owner, user, ethAmounts[i]);
|
||||||
|
|
||||||
|
vm.prank(owner);
|
||||||
|
token.mintWithETH{ value: ethAmounts[i] }(user);
|
||||||
|
|
||||||
|
assertEq(token.balanceOf(user), ethAmounts[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function test__CannotMintWithZeroETH() external {
|
||||||
|
// Anyone can call mintWithETH (public function), but it requires ETH
|
||||||
|
vm.prank(user1);
|
||||||
|
vm.expectRevert(abi.encodeWithSelector(InsufficientETH.selector));
|
||||||
|
token.mintWithETH{ value: 0 }(user2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test__MaxSupplyIsSetCorrectly() external {
|
||||||
|
// maxSupply should be set to 1000000 * 10^18 by deployment script
|
||||||
|
uint256 expectedMaxSupply = 1_000_000 * 10 ** 18;
|
||||||
|
assertEq(token.maxSupply(), expectedMaxSupply);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test__CannotMintExceedingMaxSupply() external {
|
||||||
|
uint256 currentMaxSupply = token.maxSupply();
|
||||||
|
|
||||||
|
// Try to mint more than maxSupply
|
||||||
|
vm.prank(owner);
|
||||||
|
vm.expectRevert(abi.encodeWithSelector(ExceedsMaxSupply.selector));
|
||||||
|
token.mint(user1, currentMaxSupply + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test__CannotMintWithETHExceedingMaxSupply() external {
|
||||||
|
uint256 currentMaxSupply = token.maxSupply();
|
||||||
|
// Send an amount of ETH that would exceed maxSupply when minted as tokens
|
||||||
|
uint256 ethAmount = currentMaxSupply + 1;
|
||||||
|
|
||||||
|
// Try to mint more than maxSupply with ETH
|
||||||
|
vm.deal(owner, ethAmount);
|
||||||
|
vm.prank(owner);
|
||||||
|
vm.expectRevert(abi.encodeWithSelector(ExceedsMaxSupply.selector));
|
||||||
|
token.mintWithETH{ value: ethAmount }(user1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test__OwnerCanSetMaxSupply() external {
|
||||||
|
uint256 newMaxSupply = 2_000_000 * 10 ** 18;
|
||||||
|
uint256 oldMaxSupply = token.maxSupply();
|
||||||
|
|
||||||
|
vm.expectEmit(true, true, false, false);
|
||||||
|
emit MaxSupplySet(oldMaxSupply, newMaxSupply);
|
||||||
|
|
||||||
|
vm.prank(owner);
|
||||||
|
token.setMaxSupply(newMaxSupply);
|
||||||
|
|
||||||
|
assertEq(token.maxSupply(), newMaxSupply);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test__CannotSetMaxSupplyBelowTotalSupply() external {
|
||||||
|
// First mint some tokens
|
||||||
|
uint256 mintAmount = 1000 ether;
|
||||||
|
vm.prank(owner);
|
||||||
|
token.mint(user1, mintAmount);
|
||||||
|
|
||||||
|
// Try to set maxSupply below current totalSupply
|
||||||
|
vm.prank(owner);
|
||||||
|
vm.expectRevert(abi.encodeWithSelector(ExceedsMaxSupply.selector));
|
||||||
|
token.setMaxSupply(mintAmount - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test__NonOwnerCannotSetMaxSupply() external {
|
||||||
|
uint256 newMaxSupply = 2_000_000 * 10 ** 18;
|
||||||
|
|
||||||
|
vm.prank(user1);
|
||||||
|
vm.expectRevert("Ownable: caller is not the owner");
|
||||||
|
token.setMaxSupply(newMaxSupply);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,8 +38,9 @@ contract WakuRlnV2Test is Test {
|
|||||||
|
|
||||||
// Minting a large number of tokens to not have to worry about
|
// Minting a large number of tokens to not have to worry about
|
||||||
// Not having enough balance
|
// Not having enough balance
|
||||||
|
// 900_000 ether is chosen to be well above any test requirements and is within the new max supply constraints.
|
||||||
vm.prank(address(tokenDeployer));
|
vm.prank(address(tokenDeployer));
|
||||||
token.mint(address(this), 100_000_000 ether);
|
token.mint(address(this), 900_000 ether);
|
||||||
}
|
}
|
||||||
|
|
||||||
function test__ValidRegistration__kats() external {
|
function test__ValidRegistration__kats() external {
|
||||||
@ -634,6 +635,49 @@ contract WakuRlnV2Test is Test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function test__NonMinterCanMintWithETHAndRegister() external {
|
||||||
|
uint256 idCommitment = 123;
|
||||||
|
uint32 membershipRateLimit = w.minMembershipRateLimit();
|
||||||
|
address nonMinter = vm.addr(999);
|
||||||
|
|
||||||
|
// Calculate required token amount for membership
|
||||||
|
(, uint256 price) = w.priceCalculator().calculate(membershipRateLimit);
|
||||||
|
uint256 ethAmount = price; // Use same amount of ETH as token price needed
|
||||||
|
|
||||||
|
// Verify nonMinter is not a minter
|
||||||
|
assertFalse(token.isMinter(nonMinter));
|
||||||
|
|
||||||
|
// Non-minter uses mintWithETH to get tokens needed for membership
|
||||||
|
// Need to send enough ETH to mint the required tokens (1:1 ratio)
|
||||||
|
vm.deal(nonMinter, price);
|
||||||
|
vm.prank(nonMinter);
|
||||||
|
token.mintWithETH{ value: price }(nonMinter);
|
||||||
|
|
||||||
|
// Verify tokens were minted
|
||||||
|
assertEq(token.balanceOf(nonMinter), price);
|
||||||
|
|
||||||
|
// Non-minter approves and registers for membership
|
||||||
|
vm.startPrank(nonMinter);
|
||||||
|
token.approve(address(w), price);
|
||||||
|
w.register(idCommitment, membershipRateLimit, noIdCommitmentsToErase);
|
||||||
|
vm.stopPrank();
|
||||||
|
|
||||||
|
// Verify successful registration
|
||||||
|
assertTrue(w.isInMembershipSet(idCommitment));
|
||||||
|
(uint32 fetchedRateLimit, uint32 index, uint256 rateCommitment) = w.getMembershipInfo(idCommitment);
|
||||||
|
assertEq(fetchedRateLimit, membershipRateLimit);
|
||||||
|
assertEq(index, 0);
|
||||||
|
assertNotEq(rateCommitment, 0);
|
||||||
|
|
||||||
|
// Verify membership holder is the non-minter
|
||||||
|
(,,,,,, address holder,) = w.memberships(idCommitment);
|
||||||
|
assertEq(holder, nonMinter);
|
||||||
|
|
||||||
|
// Verify tokens were transferred to membership contract
|
||||||
|
assertEq(token.balanceOf(address(w)), price);
|
||||||
|
assertEq(token.balanceOf(nonMinter), 0);
|
||||||
|
}
|
||||||
|
|
||||||
function test__WithdrawToken(uint32 membershipRateLimit) external {
|
function test__WithdrawToken(uint32 membershipRateLimit) external {
|
||||||
vm.pauseGasMetering();
|
vm.pauseGasMetering();
|
||||||
uint256 idCommitment = 2;
|
uint256 idCommitment = 2;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user