Skip to content

Commit

Permalink
BTT Testing for DropERC20 (#532)
Browse files Browse the repository at this point in the history
* create file structure

* tree: initialize

* tree: _beforeClaim

* tree:  _collectPriceOnClaim

* tree: setMaxTotalSupply

* clean

* tree: _beforeClaim

* test: initialize

* test: _beforeClaim

* test: collectPriceOnClaim

* test: canSetFunctions

* test: misc

* test: setMaxTotalSupply

* test: misc

* test: misc

* clean/lint

* remove initialize harness and use TWProxy

* clean/lint tests

---------

Co-authored-by: Yash <72552910+kumaryash90@users.noreply.github.com>
Co-authored-by: Joaquim Verges <joaquim.verges@gmail.com>
  • Loading branch information
3 people authored Oct 16, 2023
1 parent a64ca6b commit 9e5d693
Show file tree
Hide file tree
Showing 12 changed files with 906 additions and 0 deletions.
52 changes: 52 additions & 0 deletions src/test/drop/drop-erc20/_beforeClaim/_beforeClaim.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

import { DropERC20 } from "contracts/prebuilts/drop/DropERC20.sol";
import { TWProxy } from "contracts/infra/TWProxy.sol";

// Test imports
import "../../../utils/BaseTest.sol";

contract HarnessDropERC20BeforeClaim is DropERC20 {
bytes private emptyBytes = bytes("");

function harness_beforeClaim(uint256 quantity, AllowlistProof calldata _proof) public view {
_beforeClaim(address(0), quantity, address(0), 0, _proof, emptyBytes);
}
}

contract DropERC20Test_beforeClaim is BaseTest {
address public dropImp;
HarnessDropERC20BeforeClaim public proxy;

uint256 private mintQty;

function setUp() public override {
super.setUp();

bytes memory initializeData = abi.encodeCall(
DropERC20.initialize,
(deployer, NAME, SYMBOL, CONTRACT_URI, forwarders(), saleRecipient, platformFeeRecipient, platformFeeBps)
);

dropImp = address(new HarnessDropERC20BeforeClaim());
proxy = HarnessDropERC20BeforeClaim(address(new TWProxy(dropImp, initializeData)));
}

modifier setMaxTotalSupply() {
vm.prank(deployer);
proxy.setMaxTotalSupply(100);
_;
}

modifier qtyExceedMaxTotalSupply() {
mintQty = 101;
_;
}

function test_revert_MaxSupplyExceeded() public setMaxTotalSupply qtyExceedMaxTotalSupply {
DropERC20.AllowlistProof memory proof;
vm.expectRevert("exceed max total supply.");
proxy.harness_beforeClaim(mintQty, proof);
}
}
10 changes: 10 additions & 0 deletions src/test/drop/drop-erc20/_beforeClaim/_beforeClaim.tree
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
function _beforeClaim(
address,
uint256 _quantity,
address,
uint256,
AllowlistProof calldata,
bytes memory
)
└── when maxTotalSupply does not equal to 0 and totalSupply() + _quantity is greater than _maxTotalSupply
└── it should revert ✅
93 changes: 93 additions & 0 deletions src/test/drop/drop-erc20/_canSetFunctions/_canSetFunctions.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

import { DropERC20 } from "contracts/prebuilts/drop/DropERC20.sol";
import { TWProxy } from "contracts/infra/TWProxy.sol";

// Test imports
import "../../../utils/BaseTest.sol";

contract HarnessDropERC20CanSet is DropERC20 {
function canSetPlatformFeeInfo() external view returns (bool) {
return _canSetPlatformFeeInfo();
}

function canSetPrimarySaleRecipient() external view returns (bool) {
return _canSetPrimarySaleRecipient();
}

function canSetContractURI() external view returns (bool) {
return _canSetContractURI();
}

function canSetClaimConditions() external view returns (bool) {
return _canSetClaimConditions();
}
}

contract DropERC20Test_canSet is BaseTest {
address public dropImp;

HarnessDropERC20CanSet public proxy;

function setUp() public override {
super.setUp();

bytes memory initializeData = abi.encodeCall(
DropERC20.initialize,
(deployer, NAME, SYMBOL, CONTRACT_URI, forwarders(), saleRecipient, platformFeeRecipient, platformFeeBps)
);

dropImp = address(new HarnessDropERC20CanSet());
proxy = HarnessDropERC20CanSet(address(new TWProxy(dropImp, initializeData)));
}

modifier callerHasDefaultAdminRole() {
vm.startPrank(deployer);
_;
}

modifier callerDoesNotHaveDefaultAdminRole() {
_;
}

function test_canSetPlatformFee_returnTrue() public callerHasDefaultAdminRole {
bool status = proxy.canSetPlatformFeeInfo();
assertEq(status, true);
}

function test_canSetPlatformFee_returnFalse() public callerDoesNotHaveDefaultAdminRole {
bool status = proxy.canSetPlatformFeeInfo();
assertEq(status, false);
}

function test_canSetPrimarySaleRecipient_returnTrue() public callerHasDefaultAdminRole {
bool status = proxy.canSetPrimarySaleRecipient();
assertEq(status, true);
}

function test_canSetPrimarySaleRecipient_returnFalse() public callerDoesNotHaveDefaultAdminRole {
bool status = proxy.canSetPrimarySaleRecipient();
assertEq(status, false);
}

function test_canSetContractURI_returnTrue() public callerHasDefaultAdminRole {
bool status = proxy.canSetContractURI();
assertEq(status, true);
}

function test_canSetContractURI_returnFalse() public callerDoesNotHaveDefaultAdminRole {
bool status = proxy.canSetContractURI();
assertEq(status, false);
}

function test_canSetClaimConditions_returnTrue() public callerHasDefaultAdminRole {
bool status = proxy.canSetClaimConditions();
assertEq(status, true);
}

function test_canSetClaimConditions_returnFalse() public callerDoesNotHaveDefaultAdminRole {
bool status = proxy.canSetClaimConditions();
assertEq(status, false);
}
}
23 changes: 23 additions & 0 deletions src/test/drop/drop-erc20/_canSetFunctions/_canSetFunctions.tree
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
function _canSetPlatformFeeInfo()
├── when caller has DEFAULT_ADMIN_ROLE
│ └── it should return true ✅
└── when caller does not have DEFAULT_ADMIN_ROLE
└── it should return false ✅

function _canSetPrimarySaleRecipient()
├── when caller has DEFAULT_ADMIN_ROLE
│ └── it should return true ✅
└── when caller does not have DEFAULT_ADMIN_ROLE
└── it should return false ✅

function _canSetContractURI()
├── when caller has DEFAULT_ADMIN_ROLE
│ └── it should return true ✅
└── when caller does not have DEFAULT_ADMIN_ROLE
└── it should return false ✅

function _canSetClaimConditions()
├── when caller has DEFAULT_ADMIN_ROLE
│ └── it should return true ✅
└── when caller does not have DEFAULT_ADMIN_ROLE
└── it should return false ✅
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

import { DropERC20 } from "contracts/prebuilts/drop/DropERC20.sol";
import { TWProxy } from "contracts/infra/TWProxy.sol";

// Test imports
import "../../../utils/BaseTest.sol";

contract HarnessDropERC20CollectPriceOnClaim is DropERC20 {
function harness_collectPrice(
address _primarySaleRecipient,
uint256 _quantityToClaim,
address _currency,
uint256 _pricePerToken
) public payable {
_collectPriceOnClaim(_primarySaleRecipient, _quantityToClaim, _currency, _pricePerToken);
}
}

contract DropERC20Test_collectPrice is BaseTest {
address public dropImp;
HarnessDropERC20CollectPriceOnClaim public proxy;

address private currency;
address private primarySaleRecipient;
uint256 private msgValue;
uint256 private pricePerToken;

function setUp() public override {
super.setUp();

bytes memory initializeData = abi.encodeCall(
DropERC20.initialize,
(deployer, NAME, SYMBOL, CONTRACT_URI, forwarders(), saleRecipient, platformFeeRecipient, platformFeeBps)
);

dropImp = address(new HarnessDropERC20CollectPriceOnClaim());
proxy = HarnessDropERC20CollectPriceOnClaim(address(new TWProxy(dropImp, initializeData)));
}

modifier pricePerTokenZero() {
_;
}

modifier pricePerTokenNotZero() {
pricePerToken = 1 ether;
_;
}

modifier msgValueZero() {
_;
}

modifier msgValueNotZero() {
msgValue = 1 ether;
_;
}

modifier valuePriceMismatch() {
msgValue = 1 ether;
pricePerToken = 2 ether;
_;
}

modifier primarySaleRecipientZeroAddress() {
primarySaleRecipient = address(0);
_;
}

modifier primarySaleRecipientNotZeroAddress() {
primarySaleRecipient = address(0x0999);
_;
}

modifier currencyNativeToken() {
currency = NATIVE_TOKEN;
_;
}

modifier currencyNotNativeToken() {
currency = address(erc20);
_;
}

function test_revert_pricePerTokenZeroMsgValueNotZero() public pricePerTokenZero msgValueNotZero {
vm.expectRevert("!Value");
proxy.harness_collectPrice{ value: msgValue }(primarySaleRecipient, 1 ether, currency, pricePerToken);
}

function test_revert_nativeCurrencyTotalPriceZero() public pricePerTokenNotZero msgValueZero currencyNativeToken {
vm.expectRevert("quantity too low");
proxy.harness_collectPrice{ value: msgValue }(primarySaleRecipient, 0, currency, pricePerToken);
}

function test_revert_nativeCurrencyValuePriceMismatch() public currencyNativeToken valuePriceMismatch {
vm.expectRevert("Invalid msg value");
proxy.harness_collectPrice{ value: msgValue }(primarySaleRecipient, 1 ether, currency, pricePerToken);
}

function test_revert_erc20ValuePriceMismatch() public currencyNotNativeToken valuePriceMismatch {
vm.expectRevert("Invalid msg value");
proxy.harness_collectPrice{ value: msgValue }(primarySaleRecipient, 1 ether, currency, pricePerToken);
}

function test_state_nativeCurrency()
public
currencyNativeToken
pricePerTokenNotZero
msgValueNotZero
primarySaleRecipientNotZeroAddress
{
(address platformFeeRecipient, uint16 platformFeeBps) = proxy.getPlatformFeeInfo();
uint256 beforeBalancePrimarySaleRecipient = address(primarySaleRecipient).balance;
uint256 beforeBalancePlatformFeeRecipient = address(platformFeeRecipient).balance;

proxy.harness_collectPrice{ value: msgValue }(primarySaleRecipient, 1 ether, currency, pricePerToken);

uint256 afterBalancePrimarySaleRecipient = address(primarySaleRecipient).balance;
uint256 afterBalancePlatformFeeRecipient = address(platformFeeRecipient).balance;

uint256 platformFeeVal = (msgValue * platformFeeBps) / MAX_BPS;
uint256 primarySaleRecipientVal = msgValue - platformFeeVal;

assertEq(beforeBalancePrimarySaleRecipient + primarySaleRecipientVal, afterBalancePrimarySaleRecipient);
assertEq(beforeBalancePlatformFeeRecipient + platformFeeVal, afterBalancePlatformFeeRecipient);
}

function test_revert_erc20_msgValueNotZero()
public
currencyNotNativeToken
msgValueNotZero
primarySaleRecipientNotZeroAddress
{
vm.expectRevert("!Value");
proxy.harness_collectPrice{ value: msgValue }(primarySaleRecipient, msgValue, currency, pricePerToken);
}

function test_state_erc20() public currencyNotNativeToken pricePerTokenNotZero primarySaleRecipientNotZeroAddress {
(address platformFeeRecipient, uint16 platformFeeBps) = proxy.getPlatformFeeInfo();

erc20.mint(address(this), pricePerToken);
ERC20(erc20).approve(address(proxy), pricePerToken);
uint256 beforeBalancePrimarySaleRecipient = erc20.balanceOf(primarySaleRecipient);
uint256 beforeBalancePlatformFeeRecipient = erc20.balanceOf(platformFeeRecipient);

proxy.harness_collectPrice(primarySaleRecipient, pricePerToken, currency, pricePerToken);

uint256 afterBalancePrimarySaleRecipient = erc20.balanceOf(primarySaleRecipient);
uint256 afterBalancePlatformFeeRecipient = erc20.balanceOf(platformFeeRecipient);

uint256 platformFeeVal = (pricePerToken * platformFeeBps) / MAX_BPS;
uint256 primarySaleRecipientVal = 1 ether - platformFeeVal;

assertEq(beforeBalancePrimarySaleRecipient + primarySaleRecipientVal, afterBalancePrimarySaleRecipient);
assertEq(beforeBalancePlatformFeeRecipient + platformFeeVal, afterBalancePlatformFeeRecipient);
}

function test_state_erc20StoredPrimarySaleRecipient()
public
currencyNotNativeToken
pricePerTokenNotZero
primarySaleRecipientZeroAddress
{
(address platformFeeRecipient, uint16 platformFeeBps) = proxy.getPlatformFeeInfo();
address storedPrimarySaleRecipient = proxy.primarySaleRecipient();

erc20.mint(address(this), pricePerToken);
ERC20(erc20).approve(address(proxy), pricePerToken);
uint256 beforeBalancePrimarySaleRecipient = erc20.balanceOf(storedPrimarySaleRecipient);
uint256 beforeBalancePlatformFeeRecipient = erc20.balanceOf(platformFeeRecipient);

proxy.harness_collectPrice(primarySaleRecipient, pricePerToken, currency, pricePerToken);

uint256 afterBalancePrimarySaleRecipient = erc20.balanceOf(storedPrimarySaleRecipient);
uint256 afterBalancePlatformFeeRecipient = erc20.balanceOf(platformFeeRecipient);

uint256 platformFeeVal = (pricePerToken * platformFeeBps) / MAX_BPS;
uint256 primarySaleRecipientVal = 1 ether - platformFeeVal;

assertEq(beforeBalancePrimarySaleRecipient + primarySaleRecipientVal, afterBalancePrimarySaleRecipient);
assertEq(beforeBalancePlatformFeeRecipient + platformFeeVal, afterBalancePlatformFeeRecipient);
}

function test_state_nativeCurrencyStoredPrimarySaleRecipient()
public
currencyNativeToken
pricePerTokenNotZero
primarySaleRecipientZeroAddress
msgValueNotZero
{
(address platformFeeRecipient, uint16 platformFeeBps) = proxy.getPlatformFeeInfo();
address storedPrimarySaleRecipient = proxy.primarySaleRecipient();

uint256 beforeBalancePrimarySaleRecipient = address(storedPrimarySaleRecipient).balance;
uint256 beforeBalancePlatformFeeRecipient = address(platformFeeRecipient).balance;

proxy.harness_collectPrice{ value: msgValue }(primarySaleRecipient, 1 ether, currency, pricePerToken);

uint256 afterBalancePrimarySaleRecipient = address(storedPrimarySaleRecipient).balance;
uint256 afterBalancePlatformFeeRecipient = address(platformFeeRecipient).balance;

uint256 platformFeeVal = (msgValue * platformFeeBps) / MAX_BPS;
uint256 primarySaleRecipientVal = msgValue - platformFeeVal;

assertEq(beforeBalancePrimarySaleRecipient + primarySaleRecipientVal, afterBalancePrimarySaleRecipient);
assertEq(beforeBalancePlatformFeeRecipient + platformFeeVal, afterBalancePlatformFeeRecipient);
}
}
Loading

0 comments on commit 9e5d693

Please sign in to comment.