@@ -3,20 +3,23 @@ pragma solidity 0.8.26;
33
44import {Test, console2} from "forge-std/Test.sol " ;
55import {QueryTypeStakingPool} from "src/QueryTypeStakingPool.sol " ;
6+ import {QueryTypeStakerFactory} from "src/QueryTypeStakerFactory.sol " ;
67import {IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol " ;
78import {MockERC20} from "test/mocks/MockERC20.sol " ;
89import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol " ;
910
1011contract QueryTypeStakingPoolTest is Test {
1112 QueryTypeStakingPool public pool;
1213 MockERC20 public stakingToken;
14+ address public factory;
1315 address public staker;
1416 uint256 public constant INITIAL_BALANCE = 1_000_000_000 ether ;
1517 uint256 public constant MAX_TIME_SKIP = 1000 * 365 days ;
1618
1719 function setUp () public virtual {
1820 staker = makeAddr ("staker " );
1921 stakingToken = new MockERC20 ();
22+ factory = makeAddr ("factory " );
2023 bytes32 initialEntry = bytes32 (uint256 (1 ));
2124 pool = new QueryTypeStakingPool (address (this ), address (stakingToken), initialEntry);
2225
@@ -245,6 +248,25 @@ contract Stake is QueryTypeStakingPoolTest {
245248 vm.expectRevert (QueryTypeStakingPool.QueryTypeStakingPool__AmountBelowMinimum.selector );
246249 pool.stake (_amount);
247250 }
251+
252+ function testFuzz_RevertIf_AddressIsBlocklisted (uint256 _amount , uint256 _capacity ) public {
253+ _amount = bound (_amount, 1 , INITIAL_BALANCE);
254+ _capacity = bound (_capacity, _amount, type (uint256 ).max);
255+
256+ pool.setStakingTokenCapacity (_capacity);
257+
258+ // Setup initial stake to allow blocklisting
259+ vm.prank (staker);
260+ pool.stake (_amount);
261+
262+ // Blocklist the staker
263+ pool.blocklist (staker);
264+
265+ // Try to stake more
266+ vm.prank (staker);
267+ vm.expectRevert (QueryTypeStakingPool.QueryTypeStakingPool__AddressBlocklisted.selector );
268+ pool.stake (_amount);
269+ }
248270}
249271
250272contract GetConversionTableHistoryLength is QueryTypeStakingPoolTest {
@@ -281,7 +303,8 @@ contract SetStakingTokenCapacity is QueryTypeStakingPoolTest {
281303 address _notOwner ,
282304 uint256 _newCapacity
283305 ) public {
284- vm.assume (_notOwner != address (this ));
306+ vm.assume (_notOwner != address (this )); // not owner
307+
285308 vm.prank (_notOwner);
286309 vm.expectRevert (abi.encodeWithSelector (Ownable.OwnableUnauthorizedAccount.selector , _notOwner));
287310 pool.setStakingTokenCapacity (_newCapacity);
@@ -422,6 +445,38 @@ contract Unstake is QueryTypeStakingPoolTest {
422445 assertEq (pool.totalStaked (), totalStaked - _unstakeAmount);
423446 }
424447
448+ function testFuzz_RevertIf_TokenTransferFails (
449+ uint256 _stakeAmount ,
450+ uint256 _unstakeAmount ,
451+ uint256 _timeSkip ,
452+ uint256 _capacity
453+ ) public {
454+ _stakeAmount = bound (_stakeAmount, 1 , INITIAL_BALANCE);
455+ _unstakeAmount = bound (_unstakeAmount, 1 , _stakeAmount);
456+ _timeSkip = bound (_timeSkip, pool.lockupPeriod () + 1 , MAX_TIME_SKIP);
457+ _capacity = bound (_capacity, _stakeAmount, type (uint256 ).max);
458+
459+ pool.setStakingTokenCapacity (_capacity);
460+
461+ // Initial stake
462+ vm.prank (staker);
463+ pool.stake (_stakeAmount);
464+
465+ // Warp to valid unstake time
466+ vm.warp (block .timestamp + _timeSkip);
467+
468+ // Make transfer fail
469+ stakingToken.setTransferShouldFail (true );
470+
471+ vm.prank (staker);
472+ vm.expectRevert (
473+ abi.encodeWithSelector (
474+ bytes4 (keccak256 ("SafeERC20FailedOperation(address) " )), address (stakingToken)
475+ )
476+ );
477+ pool.unstake (_unstakeAmount);
478+ }
479+
425480 function testFuzz_RevertIf_StillInLockup (
426481 uint256 _stakeAmount ,
427482 uint256 _unstakeAmount ,
@@ -515,6 +570,42 @@ contract Unstake is QueryTypeStakingPoolTest {
515570 vm.prank (staker);
516571 pool.unstake (_unstakeAmount);
517572 }
573+
574+ function testFuzz_UnstakeBlockedUser (
575+ uint256 _stakeAmount ,
576+ uint256 _unstakeAmount ,
577+ uint256 _timeSkip ,
578+ uint256 _capacity
579+ ) public {
580+ _stakeAmount = bound (_stakeAmount, 1 , INITIAL_BALANCE);
581+ _unstakeAmount = bound (_unstakeAmount, 1 , _stakeAmount);
582+ _timeSkip = bound (_timeSkip, pool.lockupPeriod () + 1 , MAX_TIME_SKIP);
583+ _capacity = bound (_capacity, _stakeAmount, type (uint256 ).max);
584+
585+ pool.setStakingTokenCapacity (_capacity);
586+
587+ // Initial stake
588+ vm.prank (staker);
589+ pool.stake (_stakeAmount);
590+
591+ // Block the staker
592+ pool.blocklist (staker);
593+
594+ // Warp to valid unstake time
595+ vm.warp (block .timestamp + _timeSkip);
596+
597+ uint256 initialTotalJailed = pool.totalJailed ();
598+
599+ vm.prank (staker);
600+ pool.unstake (_unstakeAmount);
601+
602+ assertEq (
603+ pool.totalJailed (),
604+ initialTotalJailed - _unstakeAmount,
605+ "Total jailed should decrease by unstake amount "
606+ );
607+ assertEq (pool.totalStaked (), 0 , "Total staked should be zero after jailing " );
608+ }
518609}
519610
520611contract SetSigner is QueryTypeStakingPoolTest {
@@ -567,3 +658,117 @@ contract SetSigner is QueryTypeStakingPoolTest {
567658 pool.setSigner (_signer);
568659 }
569660}
661+
662+ contract Blocklist is QueryTypeStakingPoolTest {
663+ function testFuzz_BlocklistUserSuccessfully (
664+ address _user ,
665+ uint256 _stakeAmount ,
666+ uint256 _capacity
667+ ) public {
668+ vm.assume (_user != address (0 ));
669+ _stakeAmount = bound (_stakeAmount, 1 , INITIAL_BALANCE);
670+ _capacity = bound (_capacity, _stakeAmount, type (uint256 ).max);
671+
672+ pool.setStakingTokenCapacity (_capacity);
673+
674+ // Setup stake for user
675+ stakingToken.mint (_user, _stakeAmount);
676+ vm.startPrank (_user);
677+ stakingToken.approve (address (pool), _stakeAmount);
678+ pool.stake (_stakeAmount);
679+ vm.stopPrank ();
680+
681+ // Blocklist user
682+ pool.blocklist (_user);
683+
684+ assertTrue (pool.isBlocklisted (_user));
685+ assertEq (pool.totalJailed (), _stakeAmount);
686+ assertEq (pool.totalStaked (), 0 );
687+ }
688+
689+ function testFuzz_BlocklistEmitsEvents (address _user , uint256 _stakeAmount , uint256 _capacity )
690+ public
691+ {
692+ vm.assume (_user != address (0 ));
693+ _stakeAmount = bound (_stakeAmount, 1 , INITIAL_BALANCE);
694+ _capacity = bound (_capacity, _stakeAmount, type (uint256 ).max);
695+
696+ pool.setStakingTokenCapacity (_capacity);
697+
698+ // Setup stake for user
699+ stakingToken.mint (_user, _stakeAmount);
700+ vm.startPrank (_user);
701+ stakingToken.approve (address (pool), _stakeAmount);
702+ pool.stake (_stakeAmount);
703+ vm.stopPrank ();
704+
705+ vm.expectEmit ();
706+ emit QueryTypeStakingPool.StakeJailed (_user, _stakeAmount);
707+ vm.expectEmit ();
708+ emit QueryTypeStakingPool.AddressBlocklisted (_user);
709+
710+ pool.blocklist (_user);
711+ }
712+
713+ function testFuzz_RevertIf_BlocklistingAlreadyBlocklistedUser (
714+ address _user ,
715+ uint256 _stakeAmount ,
716+ uint256 _capacity
717+ ) public {
718+ vm.assume (_user != address (0 ));
719+ _stakeAmount = bound (_stakeAmount, 1 , INITIAL_BALANCE);
720+ _capacity = bound (_capacity, _stakeAmount, type (uint256 ).max);
721+
722+ pool.setStakingTokenCapacity (_capacity);
723+
724+ // Setup stake for user
725+ stakingToken.mint (_user, _stakeAmount);
726+ vm.startPrank (_user);
727+ stakingToken.approve (address (pool), _stakeAmount);
728+ pool.stake (_stakeAmount);
729+ vm.stopPrank ();
730+
731+ // First blocklist
732+ pool.blocklist (_user);
733+
734+ // Try to blocklist again
735+ vm.expectRevert (QueryTypeStakingPool.QueryTypeStakingPool__AlreadyBlocklisted.selector );
736+ pool.blocklist (_user);
737+ }
738+
739+ function testFuzz_BlocklistingUserWithNoStake (address _user ) public {
740+ vm.assume (_user != address (0 ));
741+
742+ // Blocklist user with no stake
743+ pool.blocklist (_user);
744+
745+ assertTrue (pool.isBlocklisted (_user));
746+ assertEq (pool.totalJailed (), 0 ); // No tokens to jail
747+ assertEq (pool.totalStaked (), 0 ); // No tokens staked
748+ }
749+
750+ function testFuzz_RevertIf_NotOwnerTriesToBlocklist (
751+ address _notOwner ,
752+ address _user ,
753+ uint256 _stakeAmount ,
754+ uint256 _capacity
755+ ) public {
756+ vm.assume (_notOwner != address (this )); // not owner
757+ vm.assume (_user != address (0 ));
758+ _stakeAmount = bound (_stakeAmount, 1 , INITIAL_BALANCE);
759+ _capacity = bound (_capacity, _stakeAmount, type (uint256 ).max);
760+
761+ pool.setStakingTokenCapacity (_capacity);
762+
763+ // Setup stake for user
764+ stakingToken.mint (_user, _stakeAmount);
765+ vm.startPrank (_user);
766+ stakingToken.approve (address (pool), _stakeAmount);
767+ pool.stake (_stakeAmount);
768+ vm.stopPrank ();
769+
770+ vm.prank (_notOwner);
771+ vm.expectRevert (abi.encodeWithSelector (Ownable.OwnableUnauthorizedAccount.selector , _notOwner));
772+ pool.blocklist (_user);
773+ }
774+ }
0 commit comments