diff --git a/foundry.toml b/foundry.toml index 4689a46..7efb593 100644 --- a/foundry.toml +++ b/foundry.toml @@ -8,3 +8,13 @@ remappings = [ ] # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options + +[fmt] + bracket_spacing = true + int_types = "long" + line_length = 132 + multiline_func_header = "all" + number_underscore = "preserve" + quote_style = "double" + tab_width = 4 + wrap_comments = true \ No newline at end of file diff --git a/src/Main.sol b/src/Main.sol index 6523f47..74413b8 100644 --- a/src/Main.sol +++ b/src/Main.sol @@ -1,31 +1,22 @@ // SPDX-LINCENSE-Identifier: MIT pragma solidity ^0.8.20; -pragma experimental ABIEncoderV2; - -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; -import {StakingToken} from "./StakingToken.sol"; -import {RewardToken} from "./RewardToken.sol"; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; +import { StakingToken } from "./StakingToken.sol"; +import { RewardToken } from "./RewardToken.sol"; /// @title Main Contract /// @author Gabi Maverick from catellatech /// @notice This is the main contract that manages the staking and unstaking also the rewardTokensDistribution logic -/// @dev This contract extends the Ownable contract from OpenZeppelin /// @dev This contract is used just as a educational purpose dont use it in production - contract Main is Ownable { - // custom errors - error Main_amountMustBeGreaterThanZero(); - error Main_stakingBalanceMustBeGreaterThanZero(); - - event SuccessfulStaked(address indexed user, uint256 amount); - event SuccessfulUnstake(address indexed user, uint256 amount); - + /*///////////////////////////////////////////////////////////////////////////////////////////////////////// + Variables + /////////////////////////////////////////////////////////////////////////////////////////////////////////*/ StakingToken public immutable i_stakingToken; RewardToken public immutable i_rewardToken; - address[] public stakers; - + struct StakeInfo { uint256 StakedBalance; bool hasStaked; @@ -34,11 +25,30 @@ contract Main is Ownable { mapping(address user => StakeInfo) public Stakes; + /*///////////////////////////////////////////////////////////////////////////////////////////////////////// + Events + /////////////////////////////////////////////////////////////////////////////////////////////////////////*/ + event SuccessfulStaked(address indexed user, uint256 amount); + event SuccessfulUnstake(address indexed user, uint256 amount); + + /*///////////////////////////////////////////////////////////////////////////////////////////////////////// + Custom Errors + /////////////////////////////////////////////////////////////////////////////////////////////////////////*/ + error Main_amountMustBeGreaterThanZero(); + error Main_stakingBalanceMustBeGreaterThanZero(); + + /*///////////////////////////////////////////////////////////////////////////////////////////////////////// + Constructor + /////////////////////////////////////////////////////////////////////////////////////////////////////////*/ constructor(StakingToken _stakingToken, RewardToken _rewardToken) Ownable(msg.sender) { i_stakingToken = _stakingToken; i_rewardToken = _rewardToken; } + /*///////////////////////////////////////////////////////////////////////////////////////////////////////// + Public Functions + /////////////////////////////////////////////////////////////////////////////////////////////////////////*/ + /// @notice This function is used to stake (STK) tokens /// @param _amountToStake amount of STK tokens to stake /// @dev If the amount to stake is good lets transfer StakingToken (STK) to the contract @@ -87,8 +97,9 @@ contract Main is Ownable { /// @notice This function is used to distribute the reward token (RWT) to the users who currently staking in the platform /// @dev We iterate through the stakers array and transfer the reward token (RWT) to each user - /// 🚨 this function is for educational purpuse but it can be better tbh, this can maybe in production can cause DoS - /// if a lot of users stake and the array is big enough to cause DoS, if u want to apply this in production consider Consider limiting the number of iterations in for-loops that make external calls + //**🚨 this function is for educational purpuse but it can be better tbh, this can maybe in production can cause DoSif a lot + // of users stake and the array is big enough to cause DoS, if u want to apply this in production consider Consider limiting the + // number of iterations in for-loops that make external calls*/ function rewardTokensDistribution() public onlyOwner { uint256 length = stakers.length; for (uint256 i; i < length; i++) { diff --git a/src/RewardToken.sol b/src/RewardToken.sol index 55d75c9..d37f50f 100644 --- a/src/RewardToken.sol +++ b/src/RewardToken.sol @@ -1,7 +1,7 @@ // SPDX-Lincese-Identifier: MIT pragma solidity ^0.8.20; -import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; /// @title RewardToken /// @author Gabi Maverick from catellatech diff --git a/src/StakingToken.sol b/src/StakingToken.sol index 10df973..d9b9993 100644 --- a/src/StakingToken.sol +++ b/src/StakingToken.sol @@ -1,7 +1,7 @@ // SPDX-Lincese-Identifier: MIT pragma solidity ^0.8.20; -import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; /// @title StakingToken /// @author Gabi Maverick from catellatech diff --git a/test/MainTest.t.sol b/test/MainTest.t.sol index 2b42315..7b62332 100644 --- a/test/MainTest.t.sol +++ b/test/MainTest.t.sol @@ -1,29 +1,23 @@ // SPDX-Lincese-Identifier: MIT pragma solidity ^0.8.20; -import {StakingToken} from "../src/StakingToken.sol"; -import {RewardToken} from "../src/RewardToken.sol"; -import {Main} from "../src/Main.sol"; -import {Test, console} from "forge-std/Test.sol"; - -/** -1. So i need to deploy the staking contract and fund with some STK tokens to 2 users -2. So the main token deploy reward token to use the rewardTokensDistribution function -*/ +import { StakingToken } from "../src/StakingToken.sol"; +import { RewardToken } from "../src/RewardToken.sol"; +import { Main } from "../src/Main.sol"; +import { Test } from "forge-std/Test.sol"; contract MainTest is Test { // custom errors error Main_amountMustBeGreaterThanZero(); error Main_stakingBalanceMustBeGreaterThanZero(); - event SuccessfulStaked(address indexed user, uint256 amount); event SuccessfulUnstake(address indexed user, uint256 amount); // call it all the contract that i need StakingToken public stakingToken; RewardToken public rewardToken; - Main public main; - + Main public mainContract; + address public user1 = makeAddr("user1"); address public user2 = makeAddr("user2"); address public user3 = makeAddr("user3"); @@ -34,33 +28,37 @@ contract MainTest is Test { function setUp() public { stakingToken = new StakingToken(stakingTokenInitialSupply); rewardToken = new RewardToken(rewardTokenInitialSupply); - main = new Main(stakingToken, rewardToken); + mainContract = new Main(stakingToken, rewardToken); vm.prank(address(this)); stakingToken.transfer(user1, 1_000); stakingToken.transfer(user2, 5_000); } - function testMainContractOwner() public view { - assertEq(main.owner(), address(this)); + /*///////////////////////////////////////////////////////////////////////////////////////////////////////// + CHECK OWNER TEST + /////////////////////////////////////////////////////////////////////////////////////////////////////////*/ + function testmainContractContractOwner() public view { + assertEq(mainContract.owner(), address(this)); } - // Test the staking function of the Main contract - // user1 and user2 stake some STK tokens correctly - function testStakingToken() public { + /*///////////////////////////////////////////////////////////////////////////////////////////////////////// + STAKING TOKEN TEST + /////////////////////////////////////////////////////////////////////////////////////////////////////////*/ + function testStakingToken() public { vm.startPrank(user1); uint256 amountToStakeUser1 = 555; - // Let's approve the Main contract to stake the STK tokens - stakingToken.approve(address(main), amountToStakeUser1); + // Let's approve the mainContract contract to stake the STK tokens + stakingToken.approve(address(mainContract), amountToStakeUser1); // Let's stake the STK tokens - main.stake(amountToStakeUser1); + mainContract.stake(amountToStakeUser1); vm.stopPrank(); - - // Test that the Main contract receives the STK tokens - assertEq(stakingToken.balanceOf(address(main)), amountToStakeUser1); - + + // Test that the mainContract contract receives the STK tokens + assertEq(stakingToken.balanceOf(address(mainContract)), amountToStakeUser1); + // Destructure the returned struct into individual variables for user1 - (uint256 stakedBalance, bool hasStaked, bool isStaking) = main.Stakes(user1); + (uint256 stakedBalance, bool hasStaked, bool isStaking) = mainContract.Stakes(user1); // Test that the values of the Stakes struct are updated correctly user1 assertEq(stakedBalance, amountToStakeUser1); assertEq(hasStaked, true); @@ -69,32 +67,34 @@ contract MainTest is Test { // Let's make user2 stake some STK tokens vm.startPrank(user2); uint256 amountToStakeUser2 = 4_000; - stakingToken.approve(address(main), amountToStakeUser2); - main.stake(amountToStakeUser2); + stakingToken.approve(address(mainContract), amountToStakeUser2); + mainContract.stake(amountToStakeUser2); vm.stopPrank(); - - + + // check the new balance of STK tokens in the mainContract uint256 newStakedBalance = amountToStakeUser1 + amountToStakeUser2; - assertEq(stakingToken.balanceOf(address(main)), newStakedBalance); + assertEq(stakingToken.balanceOf(address(mainContract)), newStakedBalance); // verify that user2 was succefully added into the stakers array - address staker = main.stakers(1); + address staker = mainContract.stakers(1); assertEq(staker, user2, "Staker number 2 was not added to the array."); - } - - function testStakingFailure() public { + // check that user3 can not stake with 0 amount vm.startPrank(user3); uint256 amountToStakeUser3 = 0; - stakingToken.approve(address(main), amountToStakeUser3); + stakingToken.approve(address(mainContract), amountToStakeUser3); vm.expectRevert(Main_amountMustBeGreaterThanZero.selector); - main.stake(amountToStakeUser3); + mainContract.stake(amountToStakeUser3); vm.stopPrank(); } + /*///////////////////////////////////////////////////////////////////////////////////////////////////////// + UNSTAKING TEST + /////////////////////////////////////////////////////////////////////////////////////////////////////////*/ + function testUnstake() public { uint256 amountToUnstake = 555; - // Let's call the function where users stake tokens + // Let's call the function where users stake tokens testStakingToken(); // Let's emit the event where the user unstakes @@ -103,16 +103,19 @@ contract MainTest is Test { // User1 unstake the tokens vm.startPrank(user1); - main.unstake(); + mainContract.unstake(); vm.stopPrank(); // Let's see if the user3 can unstake vm.startPrank(user3); vm.expectRevert(Main_stakingBalanceMustBeGreaterThanZero.selector); - main.unstake(); + mainContract.unstake(); vm.stopPrank(); } + /*///////////////////////////////////////////////////////////////////////////////////////////////////////// + DISTRIBUTION REWARD TOKENS TEST + /////////////////////////////////////////////////////////////////////////////////////////////////////////*/ function testRewardTokensDistribution() public { // Let's create the enviroment where exist stakers testStakingToken(); @@ -123,18 +126,13 @@ contract MainTest is Test { // Now lets make the distribution of the reward tokens to the stakers (In this case 1:1) // So if user1 staked 555 STK tokens he is going to receive 555 RWT tokens vm.startPrank(address(this)); - // We are going to send full supply to the Main contract to distribute the reward tokens to the stakers - rewardToken.transfer(address(main), rewardTokenInitialSupply); - main.rewardTokensDistribution(); + // We are going to send full supply to the mainContract contract to distribute the reward tokens to the stakers + rewardToken.transfer(address(mainContract), rewardTokenInitialSupply); + mainContract.rewardTokensDistribution(); vm.stopPrank(); // Let's check if the reward token balance is correct assertEq(rewardToken.balanceOf(user1), amountUser1Staked); assertEq(rewardToken.balanceOf(user2), amountUser2Staked); - } - - - - -} \ No newline at end of file +} diff --git a/test/RewardTokenTest.t.sol b/test/RewardTokenTest.t.sol index 2920b4b..37adf63 100644 --- a/test/RewardTokenTest.t.sol +++ b/test/RewardTokenTest.t.sol @@ -1,14 +1,13 @@ // SPDX-Lincese-Identifier: MIT pragma solidity ^0.8.20; -import {RewardToken} from "../src/RewardToken.sol"; -import {Test} from "forge-std/Test.sol"; +import { RewardToken } from "../src/RewardToken.sol"; +import { Test } from "forge-std/Test.sol"; contract RewardTokenTest is Test { RewardToken rewardToken; uint256 initialSupply = 8_000_000; - function setUp() public { rewardToken = new RewardToken(initialSupply); } @@ -16,4 +15,4 @@ contract RewardTokenTest is Test { function testRewardTokenSupply() public view { assertEq(rewardToken.totalSupply(), initialSupply); } -} \ No newline at end of file +} diff --git a/test/StakingTokenTest.t.sol b/test/StakingTokenTest.t.sol index a06797f..4c6989d 100644 --- a/test/StakingTokenTest.t.sol +++ b/test/StakingTokenTest.t.sol @@ -1,8 +1,8 @@ // SPDX-Lincese-Identifier: MIT pragma solidity ^0.8.20; -import {StakingToken} from "../src/StakingToken.sol"; -import {Test} from "forge-std/Test.sol"; +import { StakingToken } from "../src/StakingToken.sol"; +import { Test } from "forge-std/Test.sol"; contract StakingTokenTest is Test { StakingToken stakingToken; @@ -15,4 +15,4 @@ contract StakingTokenTest is Test { function testSupply() public view { assertEq(stakingToken.totalSupply(), initialStakingSupply); } -} +}