// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.19; import "forge-std/Test.sol"; import "forge-std/console.sol"; import {TwabController} from "pt-v5-twab-controller/TwabController.sol"; import "../src/Harb.sol"; import "../src/Stake.sol"; contract StakeTest is Test { TwabController tc; Harb harb; Stake stakingPool; address liquidityPool; address liquidityManager; address taxPool; event PositionCreated(uint256 indexed positionId, address indexed owner, uint256 share, uint32 creationTime, uint32 taxRate); event PositionRemoved(uint256 indexed positionId, uint256 share, uint32 lastTaxTime); function setUp() public { tc = new TwabController(60 * 60, uint32(block.timestamp)); harb = new Harb("HARB", "HARB", tc); taxPool = harb.TAX_POOL(); stakingPool = new Stake(address(harb)); harb.setStakingPool(address(stakingPool)); liquidityPool = makeAddr("liquidityPool"); harb.setLiquidityPool(liquidityPool); liquidityManager = makeAddr("liquidityManager"); harb.setLiquidityManager(liquidityManager); } function testBasicStaking() public { // Setup uint256 stakeAmount = 1 ether; address staker = makeAddr("staker"); vm.startPrank(liquidityManager); harb.mint(stakeAmount * 5); harb.transfer(staker, stakeAmount); vm.stopPrank(); vm.startPrank(staker); // Approve and stake harb.approve(address(stakingPool), stakeAmount); uint256[] memory empty; uint256 sharesExpected = stakingPool.assetsToShares(stakeAmount); vm.expectEmit(address(stakingPool)); emit PositionCreated(654321, staker, sharesExpected, uint32(block.timestamp), 1); uint256 positionId = stakingPool.snatch(stakeAmount, staker, 1, empty); // Check results assertEq(stakingPool.outstandingStake(), stakingPool.assetsToShares(stakeAmount), "Outstanding stake did not update correctly"); (uint256 share, address owner, uint32 creationTime, , uint32 taxRate) = stakingPool.positions(positionId); assertEq(stakingPool.sharesToAssets(share), stakeAmount, "Stake amount in position is incorrect"); assertEq(owner, staker, "Stake owner is incorrect"); assertEq(creationTime, uint32(block.timestamp), "Creation time is incorrect"); assertEq(taxRate, 1, "Tax rate should be initialized to 1"); vm.stopPrank(); } function testUnstaking() public { // Setup: Create a staking position first uint256 stakeAmount = 1 ether; address staker = makeAddr("staker"); vm.startPrank(liquidityManager); harb.mint(stakeAmount * 5); // Ensuring the staker has enough balance harb.transfer(staker, stakeAmount); vm.stopPrank(); // Staker stakes tokens vm.startPrank(staker); harb.approve(address(stakingPool), stakeAmount); uint256[] memory empty; uint256 positionId = stakingPool.snatch(stakeAmount, staker, 1, empty); // Simulate time passage to accumulate tax liability vm.warp(block.timestamp + 3 days); // Calculate expected tax due at the moment of unstaking uint256 taxAmount = stakingPool.taxDue(positionId, 0); uint256 assetsAfterTax = stakeAmount - taxAmount; // Expect the PositionRemoved event with the expected parameters vm.expectEmit(true, true, true, true); emit PositionRemoved(positionId, stakingPool.assetsToShares(stakeAmount - taxAmount), uint32(block.timestamp)); // Perform unstaking stakingPool.exitPosition(positionId); // Check results after unstaking assertEq(harb.balanceOf(staker), assetsAfterTax, "Assets after tax not returned correctly"); assertEq(stakingPool.outstandingStake(), 0, "Outstanding stake not updated correctly"); // Ensure the position is cleared (, address owner, uint32 time, , ) = stakingPool.positions(positionId); assertEq(time, 0, "Position time not cleared"); assertEq(owner, address(0), "Position owner not cleared"); vm.stopPrank(); } }