Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

initial Staking refactoring [WIP] #545

Draft
wants to merge 1 commit into
base: feat/new-bob-testnet
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 0 additions & 22 deletions contracts/governance/Staking/StakingProxy.sol

This file was deleted.

12 changes: 6 additions & 6 deletions contracts/governance/Staking/modules/StakingStakeModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ contract StakingStakeModule is IFunctionsList, StakingShared, CheckpointsShared,
/// @dev Retrieve the SOV tokens.
if (transferToken)
require(
SOVToken.transferFrom(sender, address(this), amount),
_SOVToken().transferFrom(sender, address(this), amount),
"Should transfer tokens successfully"
); // IS10

Expand Down Expand Up @@ -346,7 +346,7 @@ contract StakingStakeModule is IFunctionsList, StakingShared, CheckpointsShared,

/// @dev transferring total SOV amount before staking
require(
SOVToken.transferFrom(msg.sender, address(this), amount),
_SOVToken().transferFrom(msg.sender, address(this), amount),
"Should transfer tokens successfully"
); // SS10
/// @dev stakedPerInterval might lose some dust on rounding. Add it to the first staking date.
Expand Down Expand Up @@ -384,7 +384,7 @@ contract StakingStakeModule is IFunctionsList, StakingShared, CheckpointsShared,
* @return The number of tokens held.
* */
function balanceOf(address account) external view returns (uint96 balance) {
for (uint256 i = kickoffTS; i <= block.timestamp + MAX_DURATION; i += TWO_WEEKS) {
for (uint256 i = _kickoffTS(); i <= block.timestamp + MAX_DURATION; i += TWO_WEEKS) {
balance = add96(balance, _currentBalance(account, i), "Staking::balanceOf: overflow"); // S12
}
}
Expand All @@ -411,7 +411,7 @@ contract StakingStakeModule is IFunctionsList, StakingShared, CheckpointsShared,
/// @dev Calculate stakes.
uint256 count = 0;
/// @dev We need to iterate from first possible stake date after deployment to the latest from current time.
for (uint256 i = kickoffTS + TWO_WEEKS; i <= latest; i += TWO_WEEKS) {
for (uint256 i = _kickoffTS() + TWO_WEEKS; i <= latest; i += TWO_WEEKS) {
if (_currentBalance(account, i) > 0) {
count++;
}
Expand All @@ -421,7 +421,7 @@ contract StakingStakeModule is IFunctionsList, StakingShared, CheckpointsShared,

/// @dev We need to iterate from first possible stake date after deployment to the latest from current time.
uint256 j = 0;
for (uint256 i = kickoffTS + TWO_WEEKS; i <= latest; i += TWO_WEEKS) {
for (uint256 i = _kickoffTS() + TWO_WEEKS; i <= latest; i += TWO_WEEKS) {
uint96 balance = _currentBalance(account, i);
if (balance > 0) {
dates[j] = i;
Expand All @@ -437,7 +437,7 @@ contract StakingStakeModule is IFunctionsList, StakingShared, CheckpointsShared,
* @return The address of SOV token.
* */
function _getToken() internal view returns (address) {
return address(SOVToken);
return address(_SOVToken());
}

/**
Expand Down
11 changes: 10 additions & 1 deletion contracts/governance/Staking/modules/StakingStorageModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,16 @@ contract StakingStorageModule is IFunctionsList, StakingStorageShared {
return maxVestingWithdrawIterations;
}

function kickoffTS() public view returns (uint256) {
return _kickoffTS();
}

function SOVToken() public view returns (IERC20) {
return _SOVToken();
}

function getFunctionsList() external pure returns (bytes4[] memory) {
bytes4[] memory functionsList = new bytes4[](31);
bytes4[] memory functionsList = new bytes4[](32);
functionsList[0] = this.getStorageMaxDurationToStakeTokens.selector;
functionsList[1] = this.getStorageMaxVotingWeight.selector;
functionsList[2] = this.getStorageWeightFactor.selector;
Expand Down Expand Up @@ -99,6 +107,7 @@ contract StakingStorageModule is IFunctionsList, StakingStorageShared {
functionsList[28] = this.paused.selector;
functionsList[29] = this.frozen.selector;
functionsList[30] = this.getMaxVestingWithdrawIterations.selector;
functionsList[31] = this.MAX_DURATION.selector;

return functionsList;
}
Expand Down
4 changes: 2 additions & 2 deletions contracts/governance/Staking/modules/StakingVestingModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@ contract StakingVestingModule is IFunctionsList, StakingShared {
*/
function _setVestingStake(uint256 lockedTS, uint96 value) internal {
require(
lockedTS > kickoffTS,
lockedTS > _kickoffTS(),
"Invalid lock dates: must greater than contract creation timestamp"
);

// locked date must be multiples of 14 days / TWO_WEEKS
require(
(lockedTS - kickoffTS) % TWO_WEEKS == 0,
(lockedTS - _kickoffTS()) % TWO_WEEKS == 0,
"Invalid lock dates: not multiples of 14 days"
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,12 +192,12 @@ contract StakingWithdrawModule is IFunctionsList, StakingShared, CheckpointsShar
/** Burn the punished amount by sending it to 0x1 address
* @note SOV token is not allowed to be transferred to zero address
*/
SOVToken.transfer(address(0x1), punishedAmount);
_SOVToken().transfer(address(0x1), punishedAmount);
}
}

/// @dev transferFrom
bool success = SOVToken.transfer(receiver, amount);
bool success = _SOVToken().transfer(receiver, amount);
require(success, "Token transfer failed"); // S09

emit StakingWithdrawn(msg.sender, amount, until, receiver, isGovernance);
Expand Down Expand Up @@ -239,7 +239,7 @@ contract StakingWithdrawModule is IFunctionsList, StakingShared, CheckpointsShar
_decreaseDelegateStake(delegates[vesting][until], until, amount);

/// @dev transferFrom
bool success = SOVToken.transfer(receiver, amount);
bool success = _SOVToken().transfer(receiver, amount);
require(success, "Token transfer failed"); // S09

emit StakingWithdrawn(vesting, amount, until, receiver, true);
Expand Down Expand Up @@ -311,7 +311,7 @@ contract StakingWithdrawModule is IFunctionsList, StakingShared, CheckpointsShar
* */
function unlockAllTokens() external onlyOwner whenNotFrozen {
allUnlocked = true;
emit TokensUnlocked(SOVToken.balanceOf(address(this)));
emit TokensUnlocked(_SOVToken().balanceOf(address(this)));
}

/**
Expand Down
84 changes: 84 additions & 0 deletions contracts/governance/Staking/modules/proxy/StakingModulesProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.5.17;

import "../../../../proxy/modules/ModulesProxy.sol";
import "../shared/StakingStorageInitializableSlots.sol";

/**
* ModulesProxy serves as a storage processed by a set of logic contracts - modules
* Modules functions are registered in the contract's slots generated per func sig
* All the function calls except for own Proxy functions are delegated to
* the registered functions
* The ModulesProxy is designed as a universal solution for refactorig contracts
* reaching a 24K size limit (EIP-170)
*
* Upgradability is implemented at a module level to provide consistency
* It does not allow to replace separate functions - only the whole module
* meaning that if a module being registered contains other modules function signatures
* then these modulea should be replaced completely - all the functions should be removed
* to avoid leftovers or accidental replacements and therefore functional inconsistency.
*
* A module is either a new non-overlapping with registered modules
* or a complete replacement of another registered module
* in which case all the old module functions are unregistered and then
* the new module functions are registered
* There is also a separate function to unregister a module which unregisters all the functions
* There is no option to unregister a subset of module functions - one should use pausable functionality
* to achieve this
*/

contract StakingModulesProxy is ModulesProxy, StakingStorageInitializableSlots {
// Uncomment for using beforeFallback() hook
/*
bytes private constant BEFORE_FALLBACK_SIG = abi.encodeWithSignature("beforeFallback()");
bytes4 private constant BEFORE_FALLBACK_SIG_BYTES4 = bytes4(keccak256(abi.encodePacked("beforeFallback()")));
*/

constructor(address _sovToken) public {
require(_sovToken != address(0), "Invalid SOV token address");
bytes32 slotKickoffTs = STAKING_KICKOFF_TS_STORAGE_SLOT;
bytes32 slotSovTokenAddress = SOV_TOKEN_ADDRESS_STORAGE_SLOT;
uint256 kickoffTS = block.timestamp;
assembly {
sstore(slotKickoffTs, kickoffTS)
sstore(slotSovTokenAddress, _sovToken)
}
}

/**
* @notice Fallback function delegates calls to modules.
* Returns whatever the implementation call returns.
* Has a hook to execute before delegating calls
* To activate register a module with beforeFallback() function
*/
function() external payable {
/*
// Commented to safe gas by default
// Uncomment for using beforeFallback() hook
// Implement and register beforeFallback() function in a module
address beforeFallback = _getFuncImplementation(BEFORE_FALLBACK_SIG_BYTES4);
if (beforeFallback != address(0)) {
(bool success, ) = beforeFallback.delegatecall(bytes(0x39b0111a)); // abi.encodeWithSignature("beforeFallback()")
require(success, "ModulesProxy::fallback: beforeFallback() fail"); //MP02
}
*/

address target = _getFuncImplementation(msg.sig);
require(target != address(0), "ModulesProxy:target module not registered"); // MP03

bytes memory data = msg.data;
assembly {
let result := delegatecall(gas, target, add(data, 0x20), mload(data), 0, 0)
let size := returndatasize
let ptr := mload(0x40)
returndatacopy(ptr, 0, size)
switch result
case 0 {
revert(ptr, size)
}
default {
return(ptr, size)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ contract StakingShared is StakingStorageShared, SafeMath96 {
* */
function _timestampToLockDate(uint256 timestamp) internal view returns (uint256 lockDate) {
// Optimize gas costs by reading kickoffTS from storage only once.
uint256 start = kickoffTS;
uint256 start = _kickoffTS();
require(timestamp >= start, "timestamp < contract creation"); // WS23
/**
* @dev If staking timestamp does not match any of the unstaking dates
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
pragma solidity ^0.5.17;

/**
* @title Interface for Staking modules governance/Staking/modules
*/

contract StakingStorageInitializableSlots {
bytes32 internal constant STAKING_KICKOFF_TS_STORAGE_SLOT =
keccak256("STAKING_KICKOFF_TS_STORAGE_SLOT_MTo3ODc1ODoxNzc4NzM4MjA1OjE3MTQ5NzM0NjU"); // MTo3ODc1ODoxNzc4NzM4MjA1OjE3MTQ5NzM0NjU is a nonce
bytes32 internal constant SOV_TOKEN_ADDRESS_STORAGE_SLOT =
keccak256("SOV_TOKEN_ADDRESS_STORAGE_SLOT_MTo3ODc1ODoxNzc4NzM4MjA1OjE3MTQ5NzM0NjU");
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import "../../../../openzeppelin/Ownable.sol";
import "../../../../interfaces/IERC20.sol";
import "../../../IFeeSharingCollector.sol";
import "../../../Vesting/IVestingRegistry.sol";
import "./StakingStorageInitializableSlots.sol";

/**
* @title StakingStorageShared contract is inherited by Staking modules.
Expand All @@ -21,7 +22,7 @@ import "../../../Vesting/IVestingRegistry.sol";
* voting rights in the Bitocracy. Stakers are further incentivised through
* fee and slashing rewards.
* */
contract StakingStorageShared is Ownable {
contract StakingStorageShared is StakingStorageInitializableSlots, Ownable {
/// @notice 2 weeks in seconds.
uint256 constant TWO_WEEKS = 1209600;

Expand All @@ -45,13 +46,31 @@ contract StakingStorageShared is Ownable {
uint96 constant MIN_WEIGHT_SCALING = 1;
uint96 constant MAX_WEIGHT_SCALING = 9;

uint256 public constant REENTRANCY_GUARD_FREE = 1;

function _kickoffTS() internal view returns (uint256 kickoffTS) {
bytes32 slot = STAKING_KICKOFF_TS_STORAGE_SLOT;
assembly {
kickoffTS := sload(slot)
}
}

function _SOVToken() internal view returns (IERC20 SOVToken) {
bytes32 slot = SOV_TOKEN_ADDRESS_STORAGE_SLOT;
address SOVTokenAddress;
assembly {
SOVTokenAddress := sload(slot)
}
SOVToken = IERC20(SOVTokenAddress);
}

/// @notice The timestamp of contract creation. Base for the staking period calculation.
uint256 public kickoffTS;
//uint256 public kickoffTS; - moved to an unstructured storage

string name = "SOVStaking";

/// @notice The token to be staked.
IERC20 public SOVToken;
//IERC20 public SOVToken; - moved to an unstructured storage

/// @notice A record of each accounts delegate.
mapping(address => mapping(uint256 => address)) public delegates;
Expand Down
2 changes: 1 addition & 1 deletion contracts/governance/StakingRewards/StakingRewards.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ contract StakingRewards is StakingRewardsStorage {
* @notice Replacement of constructor by initialize function for Upgradable Contracts
* This function will be called only once by the owner.
* @param _SOV SOV token address
* @param _staking StakingProxy address should be passed
* @param _staking StakingModulesProxy address should be passed
* */
function initialize(address _SOV, IStaking _staking) external onlyOwner {
require(_SOV != address(0), "Invalid SOV Address.");
Expand Down
9 changes: 7 additions & 2 deletions deployment/deploy/10-deploy-StakingModulesProxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@ const func = async function (hre) {
} = hre;
const { deployer } = await getNamedAccounts();
log(col.bgYellow("Deploying StakingModulesProxy..."));
await deployWithCustomProxy(deployer, "ModulesProxy", "StakingProxy", "StakingModulesProxy");
await deploy("StakingModulesProxy", {
from: deployer,
args: [(await get("SOV")).address],
log: true,
skipIfAlreadyDeployed: true,
});
};
func.tags = ["StakingModulesProxy"];
func.dependencies = ["StakingProxy"];
func.dependencies = ["SOV"];
module.exports = func;
2 changes: 1 addition & 1 deletion deployment/deploy/1020-deploy-FeeSharingCollector.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const func = async function (hre) {
"FeeSharingCollectorProxy",
true,
[],
["0x0000000000000000000000000000000000000000", (await get("StakingProxy")).address],
["0x0000000000000000000000000000000000000000", (await get("StakingModulesProxy")).address],
"ContractsGuardianMultisig"
);
};
Expand Down
5 changes: 1 addition & 4 deletions deployment/deploy/30-deploy-AddStakingModules.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@ const func = async function () {
let modulesAddressList = [];
let totalGas = ethers.BigNumber.from(0);

const stakingProxyDeployment = await get("StakingProxy");
const stakingModulesProxyDeployment = await get("StakingModulesProxy"); //await ethers.getContract("StakingModulesProxy");
// @dev stakingModulesProxy@stakingProxy
const stakingModulesProxy = await ethers.getContract("StakingModulesProxy");

const stakingModulesProxyOwner = await stakingModulesProxy.getProxyOwner();
Expand Down Expand Up @@ -82,7 +80,7 @@ const func = async function () {
log(col.bgYellow("Generating multisig transaction to register modules..."));
await sendWithMultisig(
multisigDeployment.address,
stakingProxyDeployment.address,
stakingModulesProxyDeployment.address,
data,
deployer
);
Expand All @@ -95,7 +93,6 @@ const func = async function () {
//owned by governance - need a SIP to register
// TODO: implementation ; meanwhile use brownie sip_interaction scripts to create proposal
// TODO: figure out if possible to pass SIP via environment and run the script
//const stakingProxyDeployment = await get("StakingProxy");
if (
stakingModulesProxyOwner === timelockOwnerAddress ||
stakingModulesProxyOwner === timelockAdminAddress
Expand Down
5 changes: 1 addition & 4 deletions deployment/deploy/40-deploy-ReplaceStakingModules.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ const func = async function (hre) {
ethers,
} = hre;
const { deployer } = await getNamedAccounts();
const stakingProxyDeployment = await get("StakingProxy");
const stakingModulesProxyDeployment = await get("StakingModulesProxy"); //await ethers.getContract("StakingModulesProxy");
// @dev stakingModulesProxy@stakingProxy
const stakingModulesProxy = await ethers.getContract("StakingModulesProxy");

const modulesList = getStakingModulesNames();
Expand Down Expand Up @@ -58,7 +56,7 @@ const func = async function (hre) {
log("Generating multisig transaction to replace modules...");
await sendWithMultisig(
multisigDeployment.address,
stakingProxyDeployment.address,
stakingModulesProxyDeployment.address,
data,
deployer
);
Expand All @@ -71,7 +69,6 @@ const func = async function (hre) {
//owned by governance - need a SIP to register
// TODO: implementation ; meanwhile use brownie sip_interaction scripts to create proposal
// TODO: figure out if possible to pass SIP via environment and run the script
//const stakingProxyDeployment = await get("StakingProxy");
log(
col.bgBlue(
"Staking modules and StakingModuleProxy are deployed (those not modified are reused)"
Expand Down
Loading
Loading