Skip to content

Commit

Permalink
preventi nflation with dead shares
Browse files Browse the repository at this point in the history
  • Loading branch information
anajuliabit committed Aug 18, 2024
1 parent 1d52fdf commit a847e4c
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 19 deletions.
5 changes: 5 additions & 0 deletions script/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity 0.8.26;

import "@forge-std/Script.sol";
import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {RewardsDistributor} from "src/RewardsDistributor.sol";
import {DelegateStaking} from "src/DelegateStaking.sol";
import {Staking} from "src/Staking.sol";
Expand Down Expand Up @@ -42,6 +43,8 @@ contract Deploy is Script {
MIN_STAKE
);

IERC20Metadata(STAKING_TOKEN).approve(address(stakingProxy), 1000e18);

DelegateStaking delegate = new DelegateStaking();
delegateProxy = DelegateStaking(
address(
Expand All @@ -53,6 +56,8 @@ contract Deploy is Script {
)
);

IERC20Metadata(STAKING_TOKEN).approve(address(delegateProxy), 1000e18);

delegateProxy.initialize(
CONTRACT_OWNER,
STAKING_TOKEN,
Expand Down
24 changes: 17 additions & 7 deletions src/BaseStaking.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import {console} from "@forge-std/console.sol";
import {OwnableUpgradeable} from "@openzeppelin-upgradeable/contracts/access/OwnableUpgradeable.sol";
import {ERC20VotesUpgradeable} from "@openzeppelin-upgradeable/contracts/token/ERC20/extensions/ERC20VotesUpgradeable.sol";
import {EnumerableSetLib} from "@solady/utils/EnumerableSetLib.sol";
Expand Down Expand Up @@ -180,15 +181,22 @@ abstract contract BaseStaking is OwnableUpgradeable, ERC20VotesUpgradeable {
function convertToShares(
uint256 assets
) public view virtual returns (uint256) {
return assets.mulDivDown(totalSupply(), _totalAssets());
console.log("totoal supply", totalSupply());
console.log("total assets", _totalAssets());
console.log("assets", assets);
uint256 supply = totalSupply(); // Saves an extra SLOAD if totalSupply is non-zero.

return supply == 0 ? assets : assets.mulDivDown(supply, _totalAssets());
}

/// @notice Get the total amount of assets the shares are worth
/// @param shares The amount of shares
function convertToAssets(
uint256 shares
) public view virtual returns (uint256) {
return shares.mulDivDown(_totalAssets(), totalSupply());
uint256 supply = totalSupply(); // Saves an extra SLOAD if totalSupply is non-zero.

return supply == 0 ? shares : shares.mulDivDown(_totalAssets(), supply);
}

/// @notice Get the stake ids belonging to a user
Expand All @@ -208,20 +216,20 @@ abstract contract BaseStaking is OwnableUpgradeable, ERC20VotesUpgradeable {

/// @notice Deposit SHU into the contract
/// @param amount The amount of SHU to deposit
function _deposit(uint256 amount) internal {
function _deposit(address to, uint256 amount) internal {
// Calculate the amount of shares to mint
uint256 shares = convertToShares(amount);

// Update the total locked amount
unchecked {
totalLocked[msg.sender] += amount;
totalLocked[to] += amount;
}

// Mint the shares
_mint(msg.sender, shares);
_mint(to, shares);

// Lock the SHU in the contract
stakingToken.safeTransferFrom(msg.sender, address(this), amount);
stakingToken.safeTransferFrom(to, address(this), amount);
}

/// @notice Withdraw SHU from the contract
Expand Down Expand Up @@ -251,7 +259,9 @@ abstract contract BaseStaking is OwnableUpgradeable, ERC20VotesUpgradeable {
/// @notice Get the amount of shares that will be burned
/// @param assets The amount of assets
function _previewWithdraw(uint256 assets) internal view returns (uint256) {
return assets.mulDivUp(totalSupply(), _totalAssets());
uint256 supply = totalSupply(); // Saves an extra SLOAD if totalSupply is non-zero.

return supply == 0 ? assets : assets.mulDivUp(supply, _totalAssets());
}

/// @notice Calculates the amount to withdraw
Expand Down
20 changes: 15 additions & 5 deletions src/DelegateStaking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,18 @@ contract DelegateStaking is BaseStaking {
lockPeriod = _lockPeriod;

nextStakeId = 1;

// mint dead shares to avoid inflation attack
uint256 amount = 1000e18;

// Calculate the amount of shares to mint
uint256 shares = convertToShares(amount);

// Mint the shares to the vault
_mint(address(this), shares);

// Transfer the SHU to the vault
stakingToken.safeTransferFrom(msg.sender, address(this), amount);
}

/// @notice Stake SHU
Expand All @@ -158,12 +170,10 @@ contract DelegateStaking is BaseStaking {

require(staking.keypers(keyper), AddressIsNotAKeyper());

address user = msg.sender;

stakeId = nextStakeId++;

// Add the stake id to the user stakes
userStakes[user].add(stakeId);
userStakes[msg.sender].add(stakeId);

// Add the stake to the stakes mapping
stakes[stakeId].keyper = keyper;
Expand All @@ -176,9 +186,9 @@ contract DelegateStaking is BaseStaking {
totalDelegated[keyper] += amount;
}

_deposit(amount);
_deposit(msg.sender, amount);

emit Staked(user, keyper, amount, lockPeriod);
emit Staked(msg.sender, keyper, amount, lockPeriod);
}

/// @notice Unstake SHU
Expand Down
14 changes: 11 additions & 3 deletions src/Staking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,16 @@ contract Staking is BaseStaking {

nextStakeId = 1;

// TODO find the correct value here
_mint(address(0), 1e18);
// mint dead shares to avoid inflation attack
uint256 amount = 1000e18;
// Calculate the amount of shares to mint
uint256 shares = convertToShares(amount);

// Mint the shares to the vault
_mint(address(this), shares);

// Lock the SHU in the contract
stakingToken.safeTransferFrom(msg.sender, address(this), amount);
}

/// @notice Stake SHU
Expand Down Expand Up @@ -179,7 +187,7 @@ contract Staking is BaseStaking {
stakes[stakeId].timestamp = block.timestamp;
stakes[stakeId].lockPeriod = lockPeriod;

_deposit(amount);
_deposit(msg.sender, amount);

emit Staked(msg.sender, amount, lockPeriod);
}
Expand Down
7 changes: 5 additions & 2 deletions test/DelegateStaking.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ contract DelegateStakingTest is Test {
_jumpAhead(1234);

govToken = new MockGovToken();
_mintGovToken(address(this), 100_000_000e18);
vm.label(address(govToken), "govToken");

// deploy rewards distributor
Expand All @@ -54,6 +53,8 @@ contract DelegateStakingTest is Test {
);
vm.label(address(staking), "staking");

_mintGovToken(address(this), 2000e18);
govToken.approve(address(staking), 1000e18);
staking.initialize(
address(this), // owner
address(govToken),
Expand All @@ -72,6 +73,8 @@ contract DelegateStakingTest is Test {
);
vm.label(address(delegate), "delegate");

govToken.approve(address(delegate), 1000e18);

delegate.initialize(
address(this), // owner
address(govToken),
Expand All @@ -86,7 +89,7 @@ contract DelegateStakingTest is Test {
);

// fund reward distribution
govToken.transfer(address(rewardsDistributor), 100_000_000e18);
_mintGovToken(address(rewardsDistributor), 100_000_000e18);
}

function _setKeyper(address _keyper, bool _isKeyper) internal {
Expand Down
10 changes: 8 additions & 2 deletions test/Staking.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IER
import {TransparentUpgradeableProxy, ITransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";

import {FixedPointMathLib} from "src/libraries/FixedPointMathLib.sol";
import {Staking} from "src/Staking.sol";
import {BaseStaking} from "src/BaseStaking.sol";
Expand All @@ -33,7 +35,6 @@ contract StakingTest is Test {
_jumpAhead(1234);

govToken = new MockGovToken();
_mintGovToken(address(this), 100_000_000e18);
vm.label(address(govToken), "govToken");

// deploy rewards distributor
Expand All @@ -51,6 +52,10 @@ contract StakingTest is Test {
);
vm.label(address(staking), "staking");

_mintGovToken(address(this), 1000e18);

govToken.approve(address(staking), 1000e18);

staking.initialize(
address(this), // owner
address(govToken),
Expand All @@ -65,7 +70,8 @@ contract StakingTest is Test {
);

// fund reward distribution
govToken.transfer(address(rewardsDistributor), 100_000_000e18);

_mintGovToken(address(rewardsDistributor), 100_000_000e18);
}

function _jumpAhead(uint256 _seconds) public {
Expand Down

0 comments on commit a847e4c

Please sign in to comment.