From 7f3ac2e23ae2cfce02062f0af57da267043f31cd Mon Sep 17 00:00:00 2001 From: Abu-Miracle Date: Wed, 11 Sep 2024 16:07:52 -0700 Subject: [PATCH 1/5] test(Staking): test Deployment and Stake fumctoon --- src/ERC20.sol | 6 +- src/Staking.sol | 4 + test/ERC20.t.sol | 293 +++++++++++++++++++++++++++++++++------------ test/Staking.t.sol | 101 ++++++++++++++++ 4 files changed, 327 insertions(+), 77 deletions(-) diff --git a/src/ERC20.sol b/src/ERC20.sol index d9e424e6..b386b6d5 100644 --- a/src/ERC20.sol +++ b/src/ERC20.sol @@ -122,6 +122,10 @@ contract ERC20 is IERC20 { revert InvalidRecipient(); } + if (balanceOf[msg.sender] < amount) { + revert InsufficientBalance(); + } + unchecked { balanceOf[msg.sender] -= amount; balanceOf[recipient] += amount; @@ -160,7 +164,7 @@ contract ERC20 is IERC20 { address recipient, uint256 amount ) external returns (bool) { - require(msg.sender != recipient, "cannot transfer to self"); + //require(msg.sender != recipient, "cannot transfer to self"); if (recipient == address(0)) { revert InvalidRecipient(); } diff --git a/src/Staking.sol b/src/Staking.sol index 33782221..00c0198a 100644 --- a/src/Staking.sol +++ b/src/Staking.sol @@ -36,6 +36,10 @@ contract StakingContract { address public rewardTokenAddress; uint256 public totalStaked; + function getStakers(address _addr) public view returns (StakeDetail memory) { + return stakers[_addr]; + } + ////////////////// // CONSTANTS ////////////////// diff --git a/test/ERC20.t.sol b/test/ERC20.t.sol index a456df2d..730fc3a5 100644 --- a/test/ERC20.t.sol +++ b/test/ERC20.t.sol @@ -2,140 +2,281 @@ pragma solidity ^0.8.19; import "forge-std/Test.sol"; +import "forge-std/console.sol"; import {ERC20} from "../src/ERC20.sol"; contract ERC20ContractTest is Test { - ERC20 public erc20Contract; + ERC20 public erc20contract; address ownerAddress = address(0x0101); - address randomAddress = address(0x3892); + address scammer = address(0x9987); + address randomAddress = address(0x9333); + address recipient = address(0x5676); + address caller = address(0x2575); event Transfer(address indexed from, address indexed to, uint256 value); - event Approval( - address indexed owner, - address indexed spender, - uint256 value - ); + event Approval(address indexed owner, address indexed spender, uint256 value); + event Owned(address indexed old, address indexed newAddress); error InvalidRecipient(); + error InsufficientBalance(); function setUp() public { - vm.prank(ownerAddress); - erc20Contract = new ERC20("My Token", "MTK", 0); + vm.prank(ownerAddress); // address deploying the contract + erc20contract = new ERC20("MyToken", "MTK", 0); } - function test_ContractWasDeployedSuccessfully() public view { - assertEq(erc20Contract.name(), "My Token"); - assertEq(erc20Contract.symbol(), "MTK"); - assertEq(erc20Contract.decimals(), 0); + function test_ContractDeployedSuccessfully() public view { + assertEq(erc20contract.name(), "MyToken"); + assertEq(erc20contract.symbol(), "MTK"); + assertEq(erc20contract.decimals(), 0); + console.log(erc20contract.decimals()); } - function test_OwnerisSetCorrectly() public view { + function testFail_OwnerIsSetCorrectly() public view { + address notOwner = address(0x9998); assertEq( - erc20Contract.owner(), - ownerAddress, + erc20contract.owner(), + notOwner, "owner address not set correctly" ); } - function test_MintWillRevertWhenMintFromUnauthorizedAddress() public { - address scammer = address(0x5555); + function test_MintRevertByNonOwner() public { vm.startPrank(scammer); vm.expectRevert("Unauthorized"); - erc20Contract.mint(scammer, 1000); + erc20contract.mint(scammer, 1000); vm.stopPrank(); } - function test_MintWillRevertWhenMintToZeroAddress() public { - // Set msg.sender to `ownerAddress` + function test_MintWillRevertWhenTryingToMintToAddressZero() public { vm.prank(ownerAddress); - // Expect function call to revert vm.expectRevert(InvalidRecipient.selector); - // Mint 1000 tokens to zero address - erc20Contract.mint(address(0), 1000); + erc20contract.mint(address(0), 1000); } function test_MintWasSuccessful() public { - uint256 totalSupplyBeforeMint = erc20Contract.totalSupply(); + uint256 totalSupplyBeforeMint = erc20contract.totalSupply(); uint256 mintAmount = 1000; - // Check balance before mint + // check balance before mint assertEq( - erc20Contract.balanceOf(randomAddress), + erc20contract.balanceOf(randomAddress), 0, "expected random address balance to be 0" ); - // Set msg.sender to `ownerAddress` + // Set the msg.sender to the 'ownerAddress' vm.prank(ownerAddress); + // mint to randomAddress + erc20contract.mint(randomAddress, mintAmount); - vm.expectEmit(true, true, false, true); - emit Transfer(address(0), randomAddress, mintAmount); - // Mint 1000 tokens to random address - erc20Contract.mint(randomAddress, mintAmount); - uint256 totalSupplyAfterMint = erc20Contract.totalSupply(); - // Verify mint was successful + uint256 totalSupplyAfterMint = erc20contract.totalSupply(); + // check balance before mint assertEq( - erc20Contract.balanceOf(randomAddress), - mintAmount, - "incorrect mint amount" + erc20contract.balanceOf(randomAddress), + 1000, + "expected random address balance to be 1000" ); assertEq(totalSupplyBeforeMint + mintAmount, totalSupplyAfterMint); } function test_TransferFrom() public { - address recipient = address(0x2938); - address caller = address(0x2373); uint256 amount = 500; - // set msg.sender to owner address vm.startPrank(ownerAddress); - // Mint 500 tokens to random address - erc20Contract.mint(randomAddress, amount); - // Verify tokens was minted successfully - assertEq(erc20Contract.balanceOf(randomAddress), amount); - // Stop prank + erc20contract.mint(randomAddress, 500); + assertEq(erc20contract.balanceOf(randomAddress), 500); vm.stopPrank(); - // Set msg.sender to random address vm.startPrank(randomAddress); - - vm.expectEmit(true, true, false, true); - emit Approval(randomAddress, caller, amount); - // random address approves caller to spend `amount` tokens - erc20Contract.approve(caller, amount); - // Stop prank + erc20contract.approve(caller, amount); vm.stopPrank(); - assertEq(erc20Contract.balanceOf(recipient), 0); + assertEq(erc20contract.balanceOf(recipient), 0); - uint256 balanceOfSenderBeforeTransfer = erc20Contract.balanceOf( - randomAddress - ); - uint256 allowanceOfCallerBeforeTransfer = erc20Contract.allowance( - randomAddress, - caller - ); + uint256 balanceOfSenderBeforeTransfer = erc20contract.balanceOf(randomAddress); - vm.startPrank(caller); - erc20Contract.transferFrom(randomAddress, recipient, amount); + uint256 allowanceOfCallerBeforeTransfer = erc20contract.allowance(randomAddress, caller); - // recipient balance increased accordingly - assertEq(erc20Contract.balanceOf(recipient), amount); - // sender balance decrease accordingly - uint256 balanceOfSenderAfterTransfer = erc20Contract.balanceOf( - randomAddress - ); + vm.startPrank(caller); + erc20contract.transferFrom(randomAddress, recipient, amount); + + // recipient address increase successfully + assertEq(erc20contract.balanceOf(recipient), amount); + // randomAddress decrease successfully + uint256 balanceOfSenderAfterTransfer = erc20contract.balanceOf(randomAddress); + uint256 allowanceOfCallerAfterTransfer = erc20contract.allowance(randomAddress, caller); assertEq( - balanceOfSenderBeforeTransfer - amount, - balanceOfSenderAfterTransfer - ); - - uint256 allowanceOfCallerAfterTransfer = erc20Contract.allowance( - randomAddress, - caller + balanceOfSenderBeforeTransfer - amount, balanceOfSenderAfterTransfer ); - assertEq( allowanceOfCallerBeforeTransfer - amount, allowanceOfCallerAfterTransfer ); } + + function test_Approval() public { + vm.startPrank(ownerAddress); + + uint amount = 3000; + erc20contract.mint(ownerAddress, amount); + assertEq(erc20contract.balanceOf(ownerAddress), amount); + + vm.expectRevert(InvalidRecipient.selector); + erc20contract.approve(address(0), 2000); + + vm.stopPrank(); + } + + function test_SuccessFulApproval() public { + vm.startPrank(ownerAddress); + + uint amount = 3000; + + uint allowanceBefore = erc20contract.allowance(ownerAddress, randomAddress); + assertEq(allowanceBefore, 0); + + erc20contract.mint(ownerAddress, amount); + assertEq(erc20contract.balanceOf(ownerAddress), amount); + + erc20contract.approve(randomAddress, amount); + uint allowanceAfter = erc20contract.allowance(ownerAddress, randomAddress); + assertEq(allowanceAfter, amount); + + vm.stopPrank(); + } + + + function test_Transfer() public { + vm.startPrank(randomAddress); + vm.expectRevert(InsufficientBalance.selector); + erc20contract.transfer(recipient, 1000); + vm.stopPrank(); + + vm.startPrank(ownerAddress); + uint amount = 3000; + + uint ownerBalanceBeforeMint = erc20contract.balanceOf(ownerAddress); + + uint recipientBalanceBeforeTransfer = erc20contract.balanceOf(recipient); + + erc20contract.mint(ownerAddress, amount); + + uint ownerBalanceAfterMint = erc20contract.balanceOf(ownerAddress); + assertEq(ownerBalanceBeforeMint + amount, ownerBalanceAfterMint); + + erc20contract.transfer(recipient, 1000); + + uint recipientBalanceAfterTransfer = erc20contract.balanceOf(recipient); + assertEq(recipientBalanceBeforeTransfer + 1000, recipientBalanceAfterTransfer); + + vm.stopPrank(); + } + + function test_changeOwner() public { + vm.startPrank(randomAddress); + vm.expectRevert("Unauthorized"); + erc20contract.changeOwner(recipient); + vm.stopPrank(); + + vm.startPrank(ownerAddress); + erc20contract.changeOwner(randomAddress); + assertEq(erc20contract.owner(), randomAddress); + vm.stopPrank(); + } + + function test_Burn() public { + uint amount = 1000; + vm.startPrank(ownerAddress); + erc20contract.mint(recipient, amount); + erc20contract.burn(recipient, 20); + assertEq(erc20contract.balanceOf(recipient), amount - 20); + vm.stopPrank(); + + vm.startPrank(randomAddress); + vm.expectRevert("Unauthorized"); + erc20contract.burn(recipient, 20); + vm.stopPrank(); + } + + // EVENTS + function test_TransferEvents() public { + vm.startPrank(ownerAddress); + + erc20contract.mint(randomAddress, 1000); + assertEq(erc20contract.balanceOf(randomAddress), 1000); + + vm.stopPrank(); + + vm.startPrank(randomAddress); + vm.expectEmit(true, true, false, true); + emit Transfer(randomAddress, recipient, 500); + erc20contract.transfer(recipient, 500); + vm.stopPrank(); + } + + function test_ApprovalEvents() public { + vm.startPrank(ownerAddress); + vm.expectEmit(true, true, false, true); + emit Approval(ownerAddress, randomAddress, 1000); + erc20contract.approve(randomAddress, 1000); + vm.stopPrank(); + } + + function test_TransferFromEvents() public { + vm.startPrank(ownerAddress); + + erc20contract.mint(ownerAddress, 1000); + assertEq(erc20contract.balanceOf(ownerAddress), 1000); + + erc20contract.approve(randomAddress, 500); + vm.stopPrank(); + + vm.startPrank(randomAddress); + vm.expectEmit(true, true, false, true); + emit Transfer(ownerAddress, recipient, 500); + erc20contract.transferFrom(ownerAddress, recipient, 500); + + assertEq(erc20contract.balanceOf(recipient), 500); + vm.stopPrank(); + } + + function test_MintEvents() public { + vm.startPrank(ownerAddress); + uint totalSupplyBeforeMint = erc20contract.totalSupply(); + vm.expectEmit(true, true, false, true); + emit Transfer(address(0), randomAddress, 1000); + erc20contract.mint(randomAddress, 1000); + + uint totalSupplyAfterMint = erc20contract.totalSupply(); + assertEq(erc20contract.balanceOf(randomAddress), 1000); + assertEq(totalSupplyBeforeMint + 1000, totalSupplyAfterMint); + vm.stopPrank(); + } + + function test_BurnEvents() public { + // uint totalSupply = erc20contract.totalSupply(); + + vm.startPrank(ownerAddress); + + erc20contract.mint(randomAddress, 1000); + assertEq(erc20contract.balanceOf(randomAddress), 1000); + assertEq(erc20contract.totalSupply(), 1000); + + vm.expectEmit(true, true, false, true); + emit Transfer(randomAddress, address(0), 500); + erc20contract.burn(randomAddress, 500); + + assertEq(erc20contract.totalSupply(), 500); + vm.stopPrank(); + } + + function test_ChangeOwnerEvent() public { + vm.startPrank(ownerAddress); + vm.expectEmit(true, true, false, false); + emit Owned(ownerAddress, randomAddress); + erc20contract.changeOwner(randomAddress); + vm.stopPrank(); + + assertEq(erc20contract.owner(), randomAddress); + } } + diff --git a/test/Staking.t.sol b/test/Staking.t.sol index 9790cae2..ad30d16d 100644 --- a/test/Staking.t.sol +++ b/test/Staking.t.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; +import "forge-std/console.sol"; import {Test, console} from "forge-std/Test.sol"; import {StakingContract} from "../src/Staking.sol"; import {ERC20} from "../src/ERC20.sol"; @@ -16,7 +17,15 @@ contract StakingContractTest is Test { address receiptTokenAddress; address rewardTokenAddress; + address ownerAddr = address(0x1111); + address addr1 = address(0x8997); + address addr2 = address(0x2622); + + event TokenStaked(address indexed staker, uint256 amount, uint256 time); + event TokenWithdraw(address indexed staker, uint256 amount, uint256 time); + function setUp() public { + vm.startPrank(ownerAddr); bwcErc20TokenContract = new ERC20("BlockheaderWeb3 Token", "BWC", 0); receiptTokenContract = new ERC20("Receipt Token", "cBWC", 0); rewardTokenContract = new ERC20("Reward Token", "wBWC", 0); @@ -30,9 +39,101 @@ contract StakingContractTest is Test { receiptTokenAddress, rewardTokenAddress ); + vm.stopPrank(); } function test_StakingContractDeployment() public view { assertEq(stakingContract.bwcErc20TokenAddress(), bwcTokenAddress); + assertEq(stakingContract.receiptTokenAddress(), receiptTokenAddress); + assertEq(stakingContract.rewardTokenAddress(), rewardTokenAddress); + assertEq(stakingContract.totalStaked(), 0); + + // Making sure timeStaked, amount and status are at default values + assertEq(stakingContract.getStakers(addr1).timeStaked, 0); + assertEq(stakingContract.getStakers(addr1).amount, 0); + assertEq(stakingContract.getStakers(addr1).status, false); + } + + function test_Stake() public { + uint amount = 1000; + uint stakeAmount = 200; + uint allowance = 500; + uint time = block.timestamp; + + // Staker cannot be address zer0 + vm.startPrank(address(0)); + vm.expectRevert("STAKE: Address zero not allowed"); + stakingContract.stake(stakeAmount); + vm.stopPrank(); + + // Cannot stake zero amount + vm.startPrank(addr1); + vm.expectRevert("STAKE: Zero amount not allowed"); + stakingContract.stake(0); + // Staker does not have enough bwc tokens to stake + vm.expectRevert("STAKE: Insufficient funds"); + stakingContract.stake(stakeAmount); + vm.stopPrank(); + + // mint bwc Tokens to addr1 + vm.startPrank(ownerAddr); + bwcErc20TokenContract.mint(addr1, amount); + vm.stopPrank(); + + // Staker does not have enough receipt tokens + vm.startPrank(addr1); + // check balances of addr1 and receipt token contract + assertEq(receiptTokenContract.balanceOf(receiptTokenAddress), 0); + assertEq(bwcErc20TokenContract.balanceOf(addr1), amount); + vm.expectRevert("STAKE: Low contract receipt token balance"); + stakingContract.stake(stakeAmount); + vm.stopPrank(); + + // mint receipt token to staking contract + vm.startPrank(ownerAddr); + receiptTokenContract.mint(address(stakingContract), amount); + vm.stopPrank(); + + // Staker has not approved enough bwc tokens + vm.startPrank(addr1); + // Balance Checks + assertEq(receiptTokenContract.balanceOf(address(stakingContract)), amount); + assertEq(bwcErc20TokenContract.balanceOf(addr1), amount); + // Approving allowance tokens + bwcErc20TokenContract.approve(address(stakingContract), allowance); + vm.expectRevert("STAKE: Amount not allowed"); + // Staking 700 + stakingContract.stake(700); + vm.stopPrank(); + + // Properly update StakeDetail struct + vm.startPrank(addr1); + // Approving tokens + bwcErc20TokenContract.approve(address(stakingContract), allowance); + stakingContract.stake(stakeAmount); + + assertEq(stakingContract.getStakers(addr1).amount, stakeAmount); + assertEq(stakingContract.getStakers(addr1).timeStaked, time); + assertEq(stakingContract.getStakers(addr1).status, true); + + // check if bwc tokens have been sent to staking contract + assertEq(bwcErc20TokenContract.balanceOf(address(stakingContract)), stakeAmount); + + // check that totalStaked is incremented by amount + assertEq(stakingContract.totalStaked(), stakeAmount); + + // check the allowance remaining + uint remainingAllowance = bwcErc20TokenContract.allowance(addr1, address(stakingContract)); + assertEq(remainingAllowance, allowance - stakeAmount); + + // check that receipt tokens are sent to staker + assertEq(receiptTokenContract.balanceOf(addr1), stakeAmount); + + // Events + vm.expectEmit(true, false, true, false); + emit TokenStaked(addr1, stakeAmount, block.timestamp); + stakingContract.stake(100); + vm.stopPrank(); } + } From 07cf53862286eb0923b30a6d5ca083a204e08bb2 Mon Sep 17 00:00:00 2001 From: Abu-Miracle Date: Wed, 11 Sep 2024 21:04:34 -0700 Subject: [PATCH 2/5] test(Staking): test withdraw validations --- test/Staking.t.sol | 332 ++++++++++++++++++++++++++------------------- 1 file changed, 193 insertions(+), 139 deletions(-) diff --git a/test/Staking.t.sol b/test/Staking.t.sol index ad30d16d..4dda73a2 100644 --- a/test/Staking.t.sol +++ b/test/Staking.t.sol @@ -1,139 +1,193 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import "forge-std/console.sol"; -import {Test, console} from "forge-std/Test.sol"; -import {StakingContract} from "../src/Staking.sol"; -import {ERC20} from "../src/ERC20.sol"; - -contract StakingContractTest is Test { - StakingContract public stakingContract; - - ERC20 public bwcErc20TokenContract; - ERC20 public receiptTokenContract; - ERC20 public rewardTokenContract; - - address bwcTokenAddress; - address receiptTokenAddress; - address rewardTokenAddress; - - address ownerAddr = address(0x1111); - address addr1 = address(0x8997); - address addr2 = address(0x2622); - - event TokenStaked(address indexed staker, uint256 amount, uint256 time); - event TokenWithdraw(address indexed staker, uint256 amount, uint256 time); - - function setUp() public { - vm.startPrank(ownerAddr); - bwcErc20TokenContract = new ERC20("BlockheaderWeb3 Token", "BWC", 0); - receiptTokenContract = new ERC20("Receipt Token", "cBWC", 0); - rewardTokenContract = new ERC20("Reward Token", "wBWC", 0); - - bwcTokenAddress = address(bwcErc20TokenContract); - receiptTokenAddress = address(receiptTokenContract); - rewardTokenAddress = address(rewardTokenContract); - - stakingContract = new StakingContract( - bwcTokenAddress, - receiptTokenAddress, - rewardTokenAddress - ); - vm.stopPrank(); - } - - function test_StakingContractDeployment() public view { - assertEq(stakingContract.bwcErc20TokenAddress(), bwcTokenAddress); - assertEq(stakingContract.receiptTokenAddress(), receiptTokenAddress); - assertEq(stakingContract.rewardTokenAddress(), rewardTokenAddress); - assertEq(stakingContract.totalStaked(), 0); - - // Making sure timeStaked, amount and status are at default values - assertEq(stakingContract.getStakers(addr1).timeStaked, 0); - assertEq(stakingContract.getStakers(addr1).amount, 0); - assertEq(stakingContract.getStakers(addr1).status, false); - } - - function test_Stake() public { - uint amount = 1000; - uint stakeAmount = 200; - uint allowance = 500; - uint time = block.timestamp; - - // Staker cannot be address zer0 - vm.startPrank(address(0)); - vm.expectRevert("STAKE: Address zero not allowed"); - stakingContract.stake(stakeAmount); - vm.stopPrank(); - - // Cannot stake zero amount - vm.startPrank(addr1); - vm.expectRevert("STAKE: Zero amount not allowed"); - stakingContract.stake(0); - // Staker does not have enough bwc tokens to stake - vm.expectRevert("STAKE: Insufficient funds"); - stakingContract.stake(stakeAmount); - vm.stopPrank(); - - // mint bwc Tokens to addr1 - vm.startPrank(ownerAddr); - bwcErc20TokenContract.mint(addr1, amount); - vm.stopPrank(); - - // Staker does not have enough receipt tokens - vm.startPrank(addr1); - // check balances of addr1 and receipt token contract - assertEq(receiptTokenContract.balanceOf(receiptTokenAddress), 0); - assertEq(bwcErc20TokenContract.balanceOf(addr1), amount); - vm.expectRevert("STAKE: Low contract receipt token balance"); - stakingContract.stake(stakeAmount); - vm.stopPrank(); - - // mint receipt token to staking contract - vm.startPrank(ownerAddr); - receiptTokenContract.mint(address(stakingContract), amount); - vm.stopPrank(); - - // Staker has not approved enough bwc tokens - vm.startPrank(addr1); - // Balance Checks - assertEq(receiptTokenContract.balanceOf(address(stakingContract)), amount); - assertEq(bwcErc20TokenContract.balanceOf(addr1), amount); - // Approving allowance tokens - bwcErc20TokenContract.approve(address(stakingContract), allowance); - vm.expectRevert("STAKE: Amount not allowed"); - // Staking 700 - stakingContract.stake(700); - vm.stopPrank(); - - // Properly update StakeDetail struct - vm.startPrank(addr1); - // Approving tokens - bwcErc20TokenContract.approve(address(stakingContract), allowance); - stakingContract.stake(stakeAmount); - - assertEq(stakingContract.getStakers(addr1).amount, stakeAmount); - assertEq(stakingContract.getStakers(addr1).timeStaked, time); - assertEq(stakingContract.getStakers(addr1).status, true); - - // check if bwc tokens have been sent to staking contract - assertEq(bwcErc20TokenContract.balanceOf(address(stakingContract)), stakeAmount); - - // check that totalStaked is incremented by amount - assertEq(stakingContract.totalStaked(), stakeAmount); - - // check the allowance remaining - uint remainingAllowance = bwcErc20TokenContract.allowance(addr1, address(stakingContract)); - assertEq(remainingAllowance, allowance - stakeAmount); - - // check that receipt tokens are sent to staker - assertEq(receiptTokenContract.balanceOf(addr1), stakeAmount); - - // Events - vm.expectEmit(true, false, true, false); - emit TokenStaked(addr1, stakeAmount, block.timestamp); - stakingContract.stake(100); - vm.stopPrank(); - } - -} +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import "forge-std/console.sol"; +import {Test, console} from "forge-std/Test.sol"; +import {StakingContract} from "../src/Staking.sol"; +import {ERC20} from "../src/ERC20.sol"; + +contract StakingContractTest is Test { + StakingContract public stakingContract; + + ERC20 public bwcErc20TokenContract; + ERC20 public receiptTokenContract; + ERC20 public rewardTokenContract; + + address bwcTokenAddress; + address receiptTokenAddress; + address rewardTokenAddress; + + address ownerAddr = address(0x1111); + address addr1 = address(0x8997); + address addr2 = address(0x2622); + + event TokenStaked(address indexed staker, uint256 amount, uint256 time); + event TokenWithdraw(address indexed staker, uint256 amount, uint256 time); + + function setUp() public { + vm.startPrank(ownerAddr); + bwcErc20TokenContract = new ERC20("BlockheaderWeb3 Token", "BWC", 0); + receiptTokenContract = new ERC20("Receipt Token", "cBWC", 0); + rewardTokenContract = new ERC20("Reward Token", "wBWC", 0); + + bwcTokenAddress = address(bwcErc20TokenContract); + receiptTokenAddress = address(receiptTokenContract); + rewardTokenAddress = address(rewardTokenContract); + + stakingContract = new StakingContract( + bwcTokenAddress, + receiptTokenAddress, + rewardTokenAddress + ); + vm.stopPrank(); + } + + function test_StakingContractDeployment() public view { + assertEq(stakingContract.bwcErc20TokenAddress(), bwcTokenAddress); + assertEq(stakingContract.receiptTokenAddress(), receiptTokenAddress); + assertEq(stakingContract.rewardTokenAddress(), rewardTokenAddress); + assertEq(stakingContract.totalStaked(), 0); + + // Making sure timeStaked, amount and status are at default values + assertEq(stakingContract.getStakers(addr1).timeStaked, 0); + assertEq(stakingContract.getStakers(addr1).amount, 0); + assertEq(stakingContract.getStakers(addr1).status, false); + } + + function test_Stake() public { + uint amount = 1000; + uint stakeAmount = 200; + uint allowance = 500; + uint time = block.timestamp; + + // Staker cannot be address zer0 + vm.startPrank(address(0)); + vm.expectRevert("STAKE: Address zero not allowed"); + stakingContract.stake(stakeAmount); + vm.stopPrank(); + + // Cannot stake zero amount + vm.startPrank(addr1); + vm.expectRevert("STAKE: Zero amount not allowed"); + stakingContract.stake(0); + // Staker does not have enough bwc tokens to stake + vm.expectRevert("STAKE: Insufficient funds"); + stakingContract.stake(stakeAmount); + vm.stopPrank(); + + // mint bwc Tokens to addr1 + vm.startPrank(ownerAddr); + bwcErc20TokenContract.mint(addr1, amount); + vm.stopPrank(); + + // Staker does not have enough receipt tokens + vm.startPrank(addr1); + // check balances of addr1 and receipt token contract + assertEq(receiptTokenContract.balanceOf(receiptTokenAddress), 0); + assertEq(bwcErc20TokenContract.balanceOf(addr1), amount); + vm.expectRevert("STAKE: Low contract receipt token balance"); + stakingContract.stake(stakeAmount); + vm.stopPrank(); + + // mint receipt token to staking contract + vm.startPrank(ownerAddr); + receiptTokenContract.mint(address(stakingContract), amount); + vm.stopPrank(); + + // Staker has not approved enough bwc tokens + vm.startPrank(addr1); + // Balance Checks + assertEq(receiptTokenContract.balanceOf(address(stakingContract)), amount); + assertEq(bwcErc20TokenContract.balanceOf(addr1), amount); + // Approving allowance tokens + bwcErc20TokenContract.approve(address(stakingContract), allowance); + vm.expectRevert("STAKE: Amount not allowed"); + // Staking 700 + stakingContract.stake(700); + vm.stopPrank(); + + // Properly update StakeDetail struct + vm.startPrank(addr1); + // Approving tokens + bwcErc20TokenContract.approve(address(stakingContract), allowance); + stakingContract.stake(stakeAmount); + + assertEq(stakingContract.getStakers(addr1).amount, stakeAmount); + assertEq(stakingContract.getStakers(addr1).timeStaked, time); + assertEq(stakingContract.getStakers(addr1).status, true); + + // check if bwc tokens have been sent to staking contract + assertEq(bwcErc20TokenContract.balanceOf(address(stakingContract)), stakeAmount); + + // check that totalStaked is incremented by amount + assertEq(stakingContract.totalStaked(), stakeAmount); + + // check the allowance remaining + uint remainingAllowance = bwcErc20TokenContract.allowance(addr1, address(stakingContract)); + assertEq(remainingAllowance, allowance - stakeAmount); + + // check that receipt tokens are sent to staker + assertEq(receiptTokenContract.balanceOf(addr1), stakeAmount); + + // Events + vm.expectEmit(true, false, true, false); + emit TokenStaked(addr1, stakeAmount, block.timestamp); + stakingContract.stake(100); + vm.stopPrank(); + } + + function test_Withdraw() public { + uint amount = 1000; + uint stakeAmount = 10; + uint allowance = 700; + + // Zero address cannot withdraw + vm.startPrank(address(0)); + vm.expectRevert("WITHDRAW: Address zero not allowed"); + stakingContract.withdraw(200); + vm.stopPrank(); + + vm.startPrank(addr1); + vm.expectRevert("WITHDRAW: Zero amount not allowed"); + stakingContract.withdraw(0); + vm.stopPrank(); + + // mint bwc Tokens to addr1 + vm.startPrank(ownerAddr); + bwcErc20TokenContract.mint(addr1, amount); + vm.stopPrank(); + + // mint receipt token to staking contract + vm.startPrank(ownerAddr); + receiptTokenContract.mint(address(stakingContract), amount); + rewardTokenContract.mint(address(stakingContract), 4); + vm.stopPrank(); + + // Staker must not withdraw more than stakeAmount + vm.startPrank(addr1); + bwcErc20TokenContract.approve(address(stakingContract), allowance); + stakingContract.stake(stakeAmount); + vm.expectRevert("WITHDRAW: Withdraw amount not allowed"); + stakingContract.withdraw(1000); + vm.stopPrank(); + + // Check for proper withdrawal time + vm.startPrank(addr1); + bwcErc20TokenContract.approve(address(stakingContract), allowance); + stakingContract.stake(stakeAmount); + vm.expectRevert("WITHDRAW: Not yet time to withdraw"); + stakingContract.withdraw(7); + vm.stopPrank(); + + // Check balance of reward tokens + vm.startPrank(addr1); + bwcErc20TokenContract.approve(address(stakingContract), allowance); + receiptTokenContract.approve(address(stakingContract), allowance); + stakingContract.stake(stakeAmount); + skip(240); + vm.expectRevert("WITHDRAW: Insufficient reward token balance"); + stakingContract.withdraw(8); + vm.stopPrank(); + } + +} From 173fdaf8eae9080bd6ec83e9bdeba7d1f8afe2b4 Mon Sep 17 00:00:00 2001 From: Abu-Miracle Date: Thu, 12 Sep 2024 15:56:06 -0700 Subject: [PATCH 3/5] test(Staking): test Withdrawal function --- test/Staking.t.sol | 47 +++++++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/test/Staking.t.sol b/test/Staking.t.sol index 4dda73a2..b2b036a1 100644 --- a/test/Staking.t.sol +++ b/test/Staking.t.sol @@ -79,7 +79,7 @@ contract StakingContractTest is Test { vm.startPrank(ownerAddr); bwcErc20TokenContract.mint(addr1, amount); vm.stopPrank(); - + // Staker does not have enough receipt tokens vm.startPrank(addr1); // check balances of addr1 and receipt token contract @@ -133,12 +133,14 @@ contract StakingContractTest is Test { vm.expectEmit(true, false, true, false); emit TokenStaked(addr1, stakeAmount, block.timestamp); stakingContract.stake(100); + // To check that the stake function returns 'true' + assertEq(stakingContract.stake(100), true); vm.stopPrank(); } function test_Withdraw() public { uint amount = 1000; - uint stakeAmount = 10; + uint stakeAmount = 300; uint allowance = 700; // Zero address cannot withdraw @@ -159,35 +161,54 @@ contract StakingContractTest is Test { // mint receipt token to staking contract vm.startPrank(ownerAddr); - receiptTokenContract.mint(address(stakingContract), amount); - rewardTokenContract.mint(address(stakingContract), 4); + receiptTokenContract.mint(address(stakingContract), 400); + rewardTokenContract.mint(address(stakingContract), 150); vm.stopPrank(); // Staker must not withdraw more than stakeAmount vm.startPrank(addr1); + console.log(receiptTokenContract.balanceOf(address(stakingContract))); bwcErc20TokenContract.approve(address(stakingContract), allowance); - stakingContract.stake(stakeAmount); + stakingContract.stake(stakeAmount); vm.expectRevert("WITHDRAW: Withdraw amount not allowed"); - stakingContract.withdraw(1000); + stakingContract.withdraw(360); + console.log(receiptTokenContract.balanceOf(address(stakingContract))); vm.stopPrank(); // Check for proper withdrawal time vm.startPrank(addr1); - bwcErc20TokenContract.approve(address(stakingContract), allowance); - stakingContract.stake(stakeAmount); vm.expectRevert("WITHDRAW: Not yet time to withdraw"); - stakingContract.withdraw(7); + stakingContract.withdraw(10); vm.stopPrank(); // Check balance of reward tokens vm.startPrank(addr1); - bwcErc20TokenContract.approve(address(stakingContract), allowance); - receiptTokenContract.approve(address(stakingContract), allowance); - stakingContract.stake(stakeAmount); + receiptTokenContract.approve(address(stakingContract), 270); skip(240); vm.expectRevert("WITHDRAW: Insufficient reward token balance"); - stakingContract.withdraw(8); + stakingContract.withdraw(100); vm.stopPrank(); + + vm.startPrank(ownerAddr); + rewardTokenContract.mint(address(stakingContract), 400); + vm.stopPrank(); + // vm.startPrank(addr1); + // stakingContract.stake(stakeAmount); + // skip(240); + // vm.expectRevert("WITHDRAW: Insufficient BWC token balance"); + // stakingContract.withdraw(200); + // vm.stopPrank(); + + vm.startPrank(addr1); + vm.expectRevert("WITHDRAW: Receipt token allowance too low"); + stakingContract.withdraw(275); + vm.stopPrank(); + + // Check for proper balance reduction + vm.startPrank(addr1); + stakingContract.withdraw(200); + assertEq(stakingContract.getStakers(addr1).amount, stakeAmount - 200); + } } From 4ceebc0e150a1ec217ff3f420613edd394693fe0 Mon Sep 17 00:00:00 2001 From: Abu-Miracle Date: Fri, 13 Sep 2024 10:10:13 -0700 Subject: [PATCH 4/5] test(Staking): balance checks for withdrawal function --- test/Staking.t.sol | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/test/Staking.t.sol b/test/Staking.t.sol index b2b036a1..f9d6871c 100644 --- a/test/Staking.t.sol +++ b/test/Staking.t.sol @@ -192,23 +192,50 @@ contract StakingContractTest is Test { vm.startPrank(ownerAddr); rewardTokenContract.mint(address(stakingContract), 400); vm.stopPrank(); - // vm.startPrank(addr1); - // stakingContract.stake(stakeAmount); - // skip(240); - // vm.expectRevert("WITHDRAW: Insufficient BWC token balance"); - // stakingContract.withdraw(200); - // vm.stopPrank(); vm.startPrank(addr1); vm.expectRevert("WITHDRAW: Receipt token allowance too low"); stakingContract.withdraw(275); vm.stopPrank(); - // Check for proper balance reduction vm.startPrank(addr1); + uint balanceBeforeWithdraw = receiptTokenContract.balanceOf(address(stakingContract)); + + uint bwcBalanceBeforeWithdraw = bwcErc20TokenContract.balanceOf(addr1); + + uint totalStakedBeforeWithdraw = stakingContract.totalStaked(); stakingContract.withdraw(200); + + // Check for proper balance reduction assertEq(stakingContract.getStakers(addr1).amount, stakeAmount - 200); + uint balanceAfterWithdraw = receiptTokenContract.balanceOf(address(stakingContract)); + + uint bwcBalanceAfterWithdraw = bwcErc20TokenContract.balanceOf(addr1); + + uint totalStakedAfterWithdraw = stakingContract.totalStaked(); + // Check successful transfer of receipt tokens From addr1 to stakingContract + assertEq(balanceBeforeWithdraw + 200, balanceAfterWithdraw); + + // Check successful transfer of reward tokens to addr1 + assertEq(rewardTokenContract.balanceOf(addr1), 400); + // Check successful transfer of bwc tokens to addr1 + assertEq(bwcBalanceBeforeWithdraw + 200, bwcBalanceAfterWithdraw); + // Check that totalStaked is properly deducted + assertEq(totalStakedBeforeWithdraw - 200, totalStakedAfterWithdraw); + + console.log(bwcErc20TokenContract.balanceOf(addr1)); + vm.stopPrank(); + + // Withdraw Events + vm.startPrank(addr1); + vm.expectEmit(true, false, true, false); + emit TokenWithdraw(addr1, 50, block.timestamp); + stakingContract.withdraw(50); + + // Check that Withdraw function returns true + assertEq(stakingContract.withdraw(10), true); + vm.stopPrank(); } } From 562497a60683936e57f6ce1bd0a3084f19305167 Mon Sep 17 00:00:00 2001 From: Abu-Miracle Date: Fri, 13 Sep 2024 12:49:24 -0700 Subject: [PATCH 5/5] Removed console logs --- test/Staking.t.sol | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/test/Staking.t.sol b/test/Staking.t.sol index f9d6871c..fc3f6163 100644 --- a/test/Staking.t.sol +++ b/test/Staking.t.sol @@ -130,7 +130,7 @@ contract StakingContractTest is Test { assertEq(receiptTokenContract.balanceOf(addr1), stakeAmount); // Events - vm.expectEmit(true, false, true, false); + vm.expectEmit(true, false, false, false); emit TokenStaked(addr1, stakeAmount, block.timestamp); stakingContract.stake(100); // To check that the stake function returns 'true' @@ -167,12 +167,12 @@ contract StakingContractTest is Test { // Staker must not withdraw more than stakeAmount vm.startPrank(addr1); - console.log(receiptTokenContract.balanceOf(address(stakingContract))); + bwcErc20TokenContract.approve(address(stakingContract), allowance); stakingContract.stake(stakeAmount); vm.expectRevert("WITHDRAW: Withdraw amount not allowed"); stakingContract.withdraw(360); - console.log(receiptTokenContract.balanceOf(address(stakingContract))); + vm.stopPrank(); // Check for proper withdrawal time @@ -224,13 +224,12 @@ contract StakingContractTest is Test { // Check that totalStaked is properly deducted assertEq(totalStakedBeforeWithdraw - 200, totalStakedAfterWithdraw); - console.log(bwcErc20TokenContract.balanceOf(addr1)); vm.stopPrank(); // Withdraw Events vm.startPrank(addr1); - vm.expectEmit(true, false, true, false); - emit TokenWithdraw(addr1, 50, block.timestamp); + vm.expectEmit(true, false, false, false); + emit TokenWithdraw(addr1, 70, block.timestamp); stakingContract.withdraw(50); // Check that Withdraw function returns true