diff --git a/contracts/mock/tokens/StETHMock.sol b/contracts/mock/tokens/StETHMock.sol index bfb7f7d..cc4ae37 100644 --- a/contracts/mock/tokens/StETHMock.sol +++ b/contracts/mock/tokens/StETHMock.sol @@ -2,27 +2,95 @@ pragma solidity ^0.8.20; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import {PRECISION} from "@solarity/solidity-lib/utils/Globals.sol"; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; -contract StETHMock is ERC20 { - uint256 public totalPooledEther = PRECISION; +contract StETHMock is ERC20, Ownable { + uint256 public totalShares; + uint256 public totalPooledEther; - constructor() ERC20("Staked Ether", "stETH") {} + mapping(address => uint256) private shares; - function mint(address account_, uint256 amount_) external { - _mint(account_, amount_); + constructor() ERC20("Staked Ether Mock", "stETHMock") { + _mintShares(address(this), 10 ** decimals()); + + totalPooledEther = 10 ** decimals(); + } + + function mint(address _account, uint256 _amount) external { + require(_amount <= 1000 * (10 ** decimals()), "StETHMock: amount is too big"); + + uint256 sharesAmount = getSharesByPooledEth(_amount); + + _mintShares(_account, sharesAmount); + + totalPooledEther += _amount; + } + + function setTotalPooledEther(uint256 _totalPooledEther) external onlyOwner { + totalPooledEther = _totalPooledEther; } - function _transfer(address sender_, address recipient_, uint256 amount_) internal override { - amount_ = (amount_ * PRECISION) / totalPooledEther; - super._transfer(sender_, recipient_, amount_); + function totalSupply() public view override returns (uint256) { + return totalPooledEther; } - function setTotalPooledEther(uint256 totalPooledEther_) external { - totalPooledEther = totalPooledEther_; + function balanceOf(address _account) public view override returns (uint256) { + return getPooledEthByShares(_sharesOf(_account)); } - function balanceOf(address account_) public view override returns (uint256) { - return (super.balanceOf(account_) * totalPooledEther) / PRECISION; + function sharesOf(address _account) external view returns (uint256) { + return _sharesOf(_account); + } + + function getSharesByPooledEth(uint256 _ethAmount) public view returns (uint256) { + return (_ethAmount * totalShares) / totalPooledEther; + } + + function getPooledEthByShares(uint256 _sharesAmount) public view returns (uint256) { + return (_sharesAmount * totalPooledEther) / totalShares; + } + + function transferShares(address _recipient, uint256 _sharesAmount) external returns (uint256) { + _transferShares(msg.sender, _recipient, _sharesAmount); + uint256 tokensAmount = getPooledEthByShares(_sharesAmount); + return tokensAmount; + } + + function transferSharesFrom(address _sender, address _recipient, uint256 _sharesAmount) external returns (uint256) { + uint256 tokensAmount = getPooledEthByShares(_sharesAmount); + _spendAllowance(_sender, msg.sender, tokensAmount); + _transferShares(_sender, _recipient, _sharesAmount); + return tokensAmount; + } + + function _transfer(address _sender, address _recipient, uint256 _amount) internal override { + uint256 _sharesToTransfer = getSharesByPooledEth(_amount); + _transferShares(_sender, _recipient, _sharesToTransfer); + } + + function _sharesOf(address _account) internal view returns (uint256) { + return shares[_account]; + } + + function _transferShares(address _sender, address _recipient, uint256 _sharesAmount) internal { + require(_sender != address(0), "TRANSFER_FROM_ZERO_ADDR"); + require(_recipient != address(0), "TRANSFER_TO_ZERO_ADDR"); + require(_recipient != address(this), "TRANSFER_TO_STETH_CONTRACT"); + + uint256 currentSenderShares = shares[_sender]; + require(_sharesAmount <= currentSenderShares, "BALANCE_EXCEEDED"); + + shares[_sender] = currentSenderShares - _sharesAmount; + shares[_recipient] += _sharesAmount; + } + + function _mintShares(address _recipient, uint256 _sharesAmount) internal returns (uint256 newTotalShares) { + require(_recipient != address(0), "MINT_TO_ZERO_ADDR"); + + totalShares += _sharesAmount; + + shares[_recipient] += _sharesAmount; + + return totalShares; } } diff --git a/contracts/mock/tokens/WStETHMock.sol b/contracts/mock/tokens/WStETHMock.sol index 628f765..4b7daaa 100644 --- a/contracts/mock/tokens/WStETHMock.sol +++ b/contracts/mock/tokens/WStETHMock.sol @@ -8,7 +8,7 @@ import {IStETH} from "../../interfaces/tokens/IStETH.sol"; contract WStETHMock is ERC20 { IStETH public stETH; - constructor(address stETH_) ERC20("Wraped Staked Ether", "WStETH") { + constructor(address stETH_) ERC20("Wraped Staked Ether Mock", "WStETHMock") { stETH = IStETH(stETH_); } diff --git a/deploy/data/config.json b/deploy/data/config.json index d9f2514..7eac752 100644 --- a/deploy/data/config.json +++ b/deploy/data/config.json @@ -10,6 +10,7 @@ "decreaseInterval": 86400, "withdrawLockPeriod": 120, "claimLockPeriod": 60, + "withdrawLockPeriodAfterStake": 30, "initialReward": "14400000000000000000000", "rewardDecrease": "2468994701000000000", "minimalStake": "1000000000000000", @@ -20,6 +21,7 @@ "decreaseInterval": 60, "withdrawLockPeriod": 1, "claimLockPeriod": 1, + "withdrawLockPeriodAfterStake": 30, "initialReward": "100000000000000000000", "rewardDecrease": "100000000000000000000", "minimalStake": "1000000000000000", diff --git a/deploy/data/config_goerli.json b/deploy/data/config_goerli.json index 614909d..9891da6 100644 --- a/deploy/data/config_goerli.json +++ b/deploy/data/config_goerli.json @@ -10,6 +10,7 @@ "decreaseInterval": 86400, "withdrawLockPeriod": 120, "claimLockPeriod": 60, + "withdrawLockPeriodAfterStake": 30, "initialReward": "14400000000000000000000", "rewardDecrease": "2468994701000000000", "minimalStake": "1000000000000000", @@ -20,6 +21,7 @@ "decreaseInterval": 60, "withdrawLockPeriod": 1, "claimLockPeriod": 1, + "withdrawLockPeriodAfterStake": 30, "initialReward": "100000000000000000000", "rewardDecrease": "100000000000000000000", "minimalStake": "1000000000000000", diff --git a/deploy/data/config_localhost.json b/deploy/data/config_localhost.json index 54aca13..4c965c2 100644 --- a/deploy/data/config_localhost.json +++ b/deploy/data/config_localhost.json @@ -10,6 +10,7 @@ "decreaseInterval": 86400, "withdrawLockPeriod": 120, "claimLockPeriod": 60, + "withdrawLockPeriodAfterStake": 30, "initialReward": "14400000000000000000000", "rewardDecrease": "2468994701000000000", "minimalStake": "1000000000000000", @@ -20,6 +21,7 @@ "decreaseInterval": 60, "withdrawLockPeriod": 1, "claimLockPeriod": 1, + "withdrawLockPeriodAfterStake": 30, "initialReward": "100000000000000000000", "rewardDecrease": "100000000000000000000", "minimalStake": "1000000000000000", diff --git a/deploy/data/config_sepolia.json b/deploy/data/config_sepolia.json index b41fe6f..e07087a 100644 --- a/deploy/data/config_sepolia.json +++ b/deploy/data/config_sepolia.json @@ -10,6 +10,7 @@ "decreaseInterval": 86400, "withdrawLockPeriod": 120, "claimLockPeriod": 60, + "withdrawLockPeriodAfterStake": 30, "initialReward": "14400000000000000000000", "rewardDecrease": "2468994701000000000", "minimalStake": "10000000000", @@ -20,6 +21,7 @@ "decreaseInterval": 60, "withdrawLockPeriod": 1, "claimLockPeriod": 1, + "withdrawLockPeriodAfterStake": 30, "initialReward": "100000000000000000000", "rewardDecrease": "100000000000000000000", "minimalStake": "10000000000", @@ -38,7 +40,7 @@ "L2": { "swapRouter": "0xE592427A0AEce92De3Edee1F18E0157C05861564", "nonfungiblePositionManager": "0xC36442b4a4522E871399CD717aBDD847Ab11FE88", - "wStEth": "0xed2f149221ECf2Cf4e929560B8E42a94555c3986" + "wStEth": "0xfD5d8Dc9918e04673b9F5F332cE63BcC3BE427a2" }, "swapParams": { "fee": 10000, diff --git a/package.json b/package.json index bd1fef6..0b4b780 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "private-network-fork": "npx hardhat node --fork https://mainnet.infura.io/v3/$(grep INFURA_KEY .env | cut -d '\"' -f2)", "deploy-localhost": "npx hardhat migrate --network localhost", "deploy-goerli": "npx hardhat migrate --network goerli --verify", - "deploy-sepolia": "npx hardhat migrate --network sepolia --verify --continue", + "deploy-sepolia": "npx hardhat migrate --network sepolia --verify", "deploy-chapel": "npx hardhat migrate --network chapel --verify", "deploy-mumbai": "npx hardhat migrate --network mumbai --verify", "deploy-fuji": "npx hardhat migrate --network fuji --verify", diff --git a/test/Distribution.test.ts b/test/Distribution.test.ts index 1807624..d063234 100644 --- a/test/Distribution.test.ts +++ b/test/Distribution.test.ts @@ -181,7 +181,6 @@ describe('Distribution', () => { depositToken.approve(distribution, wei(1000)), depositToken.connect(SECOND).approve(distribution, wei(1000)), ]); - await l1Sender.transferOwnership(distribution); await reverter.snapshot(); @@ -1312,7 +1311,7 @@ describe('Distribution', () => { expect(await depositToken.balanceOf(distribution)).to.eq(wei(20)); await setNextTime(oneDay + oneDay); - await depositToken.setTotalPooledEther(wei(0.8, 25)); + await depositToken.setTotalPooledEther(((await depositToken.totalPooledEther()) * 8n) / 10n); expect(await depositToken.balanceOf(distribution)).to.eq(wei(16)); let tx = await distribution.withdraw(poolId, wei(999)); @@ -1590,7 +1589,7 @@ describe('Distribution', () => { it('should return overplus if deposited token increased', async () => { await distribution.stake(0, wei(1)); - await depositToken.setTotalPooledEther(wei(2, 25)); + await depositToken.setTotalPooledEther((await depositToken.totalPooledEther()) * 2n); let overplus = await distribution.overplus(); expect(overplus).to.eq(wei(1)); @@ -1600,12 +1599,12 @@ describe('Distribution', () => { overplus = await distribution.overplus(); expect(overplus).to.eq(wei(1)); - await depositToken.setTotalPooledEther(wei(1, 25)); + await depositToken.setTotalPooledEther((await depositToken.totalPooledEther()) / 2n); overplus = await distribution.overplus(); expect(overplus).to.eq(0); - await depositToken.setTotalPooledEther(wei(5, 25)); + await depositToken.setTotalPooledEther((await depositToken.totalPooledEther()) * 5n); overplus = await distribution.overplus(); expect(overplus).to.eq(wei(5.5)); @@ -1626,7 +1625,7 @@ describe('Distribution', () => { await distribution.stake(1, wei(1)); - await depositToken.setTotalPooledEther(wei(2, 25)); + await depositToken.setTotalPooledEther((await depositToken.totalPooledEther()) * 2n); const overplus = await distribution.overplus(); expect(overplus).to.eq(wei(1));