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

refactor: account for staking contract deposit value #186

Merged
merged 16 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from 8 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: 10 additions & 12 deletions contracts/staking/StakingFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -328,22 +328,20 @@ contract StakingFactory {
/// @param instance Staking proxy instance.
/// @return amount Emissions amount.
function verifyInstanceAndGetEmissionsAmount(address instance) external view returns (uint256 amount) {
// Verify the proxy instance
bool success = verifyInstance(instance);
DavidMinarsch marked this conversation as resolved.
Show resolved Hide resolved
// Verify the proxy instance by checking its corresponding implementation
// If the implementation exists, the proxy instance has been created by this factory and verified initially
DavidMinarsch marked this conversation as resolved.
Show resolved Hide resolved
address implementation = mapInstanceParams[instance].implementation;

if (success) {
// Get the proxy instance emissions amount
amount = IStaking(instance).emissionsAmount();

// If there is a verifier, adjust the amount
// Check that the implementation corresponds to the proxy instance
if (implementation != address(0)) {
// If there is a verifier, get the emissions amount
address localVerifier = verifier;
if (localVerifier != address(0)) {
// Get the max possible emissions amount
uint256 maxEmissions = IStakingVerifier(localVerifier).getEmissionsAmountLimit(instance);
// Limit excessive emissions amount
if (amount > maxEmissions) {
amount = maxEmissions;
}
amount = IStakingVerifier(localVerifier).getEmissionsAmountLimit(instance);
} else {
// Get the proxy instance emissions amount
amount = IStaking(instance).emissionsAmount();
}
}
}
Expand Down
88 changes: 62 additions & 26 deletions contracts/staking/StakingVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ interface IStaking {
/// @return Maximum number of services.
function maxNumServices() external view returns (uint256);

/// @dev Gets time for emissions.
/// @return Time for emissions.
function timeForEmissions() external view returns (uint256);

/// @dev Gets emissions amount.
/// @return Emissions amount.
function emissionsAmount() external view returns (uint256);

/// @dev Gets service staking token.
/// @return Service staking token address.
function stakingToken() external view returns (address);
Expand All @@ -22,6 +30,10 @@ interface IStaking {
/// @dev Gets service registry token utility address.
/// @return Service registry token utility address.
function serviceRegistryTokenUtility() external view returns(address);

/// @dev Minimum service staking deposit value required for staking.
/// @return Minimum service staking deposit.
function minStakingDeposit() external view returns(uint256);
}

/// @dev Provided zero address.
Expand Down Expand Up @@ -52,24 +64,26 @@ contract StakingVerifier {
event OwnerUpdated(address indexed owner);
event SetImplementationsCheck(bool setCheck);
event ImplementationsWhitelistUpdated(address[] implementations, bool[] statuses, bool setCheck);
event StakingLimitsUpdated(uint256 rewardsPerSecondLimit, uint256 timeForEmissionsLimit, uint256 _numServicesLimit,
uint256 emissionsLimit);
event StakingLimitsUpdated(uint256 minStakingDepositLimit, uint256 timeForEmissionsLimit, uint256 numServicesLimit,
uint256 apyLimit);

// One year constant
uint256 public constant ONE_YEAR = 1 days * 365;
// OLAS token address
address public immutable olas;
// Service registry address
address public immutable serviceRegistry;
// Service registry token utility
address public immutable serviceRegistryTokenUtility;

// Rewards per second limit
uint256 public rewardsPerSecondLimit;
// Minimum staking deposit limit
uint256 public minStakingDepositLimit;
// Time for emissions limit
uint256 public timeForEmissionsLimit;
// Limit for the number of services
uint256 public numServicesLimit;
// Emissions per service limit
uint256 public emissionsLimit;
// APY limit in 1e18 format
uint256 public apyLimit;
// Contract owner address
address public owner;
// Flag to check for the implementation address whitelisting status
Expand All @@ -82,35 +96,37 @@ contract StakingVerifier {
/// @param _olas OLAS token address.
/// @param _serviceRegistry Service registry address.
/// @param _serviceRegistryTokenUtility Service registry token utility address.
/// @param _rewardsPerSecondLimit Rewards per second limit.
/// @param _minStakingDepositLimit Minimum staking deposit limit.
/// @param _timeForEmissionsLimit Time for emissions limit.
/// @param _numServicesLimit Limit for the number of services.
/// @param _apyLimit APY limit in 1e18 format.
constructor(
address _olas,
address _serviceRegistry,
address _serviceRegistryTokenUtility,
uint256 _rewardsPerSecondLimit,
uint256 _minStakingDepositLimit,
uint256 _timeForEmissionsLimit,
uint256 _numServicesLimit
uint256 _numServicesLimit,
uint256 _apyLimit
) {
// Zero address check
if (_olas == address(0) || _serviceRegistry == address(0)) {
revert ZeroAddress();
}

// Zero values check
if (_rewardsPerSecondLimit == 0 || _timeForEmissionsLimit == 0 || _numServicesLimit == 0) {
if (_minStakingDepositLimit == 0 || _timeForEmissionsLimit == 0 || _numServicesLimit == 0 || _apyLimit == 0) {
revert ZeroValue();
}

owner = msg.sender;
olas = _olas;
serviceRegistry = _serviceRegistry;
serviceRegistryTokenUtility = _serviceRegistryTokenUtility;
rewardsPerSecondLimit = _rewardsPerSecondLimit;
minStakingDepositLimit = _minStakingDepositLimit;
timeForEmissionsLimit = _timeForEmissionsLimit;
numServicesLimit = _numServicesLimit;
emissionsLimit = _rewardsPerSecondLimit * _timeForEmissionsLimit * _numServicesLimit;
apyLimit = _apyLimit;
}

/// @dev Changes the owner address.
Expand Down Expand Up @@ -225,10 +241,27 @@ contract StakingVerifier {
return false;
}

// Check for the staking parameters
// Check for minimum staking deposit
DavidMinarsch marked this conversation as resolved.
Show resolved Hide resolved
// Get instance min staking deposit
uint256 minStakingDeposit = IStaking(instance).minStakingDeposit();
if (minStakingDeposit > minStakingDepositLimit) {
return false;
}

// Calculate rewards per year
uint256 rewardsPerYear = IStaking(instance).rewardsPerSecond() * ONE_YEAR;
// Calculate current APY in 1e18 format
uint256 apy = (rewardsPerYear * 1e18) / minStakingDeposit;

// Compare APY with the limit
DavidMinarsch marked this conversation as resolved.
Show resolved Hide resolved
if (apy > apyLimit) {
return false;
}

// Check for time for emissions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Check for time for emissions
// Check for time for emissions. This lets the verifier enforce an upper bound on the emissions length for risk mitigation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kupermind confirming that timeForEmissions is related to activity period?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's related to emissionsAmount of instance that requests that amount from the incentive dispenser.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking of livenessPeriod.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add the relevant getters so a future Verifier can also impose constrains on livenessPeriod?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean add the livenessPeriodLimit variable?

// This is a must have parameter for all staking contracts
uint256 rewardsPerSecond = IStaking(instance).rewardsPerSecond();
if (rewardsPerSecond > rewardsPerSecondLimit) {
uint256 timeForEmissions = IStaking(instance).timeForEmissions();
if (timeForEmissions > timeForEmissionsLimit) {
return false;
}

Expand Down Expand Up @@ -279,36 +312,39 @@ contract StakingVerifier {
}

/// @dev Changes staking parameter limits.
/// @param _rewardsPerSecondLimit Rewards per second limit.
/// @param _minStakingDepositLimit Minimum staking deposit limit.
/// @param _timeForEmissionsLimit Time for emissions limit.
/// @param _numServicesLimit Limit for the number of services.
/// @param _apyLimit APY limit in 1e18 format.
function changeStakingLimits(
uint256 _rewardsPerSecondLimit,
uint256 _minStakingDepositLimit,
uint256 _timeForEmissionsLimit,
uint256 _numServicesLimit
uint256 _numServicesLimit,
uint256 _apyLimit
) external {
// Check the contract ownership
if (owner != msg.sender) {
revert OwnerOnly(owner, msg.sender);
}

// Zero values check
if (_rewardsPerSecondLimit == 0 || _timeForEmissionsLimit == 0 || _numServicesLimit == 0) {
if (_minStakingDepositLimit == 0 || _timeForEmissionsLimit == 0 || _numServicesLimit == 0 || _apyLimit == 0) {
revert ZeroValue();
}

DavidMinarsch marked this conversation as resolved.
Show resolved Hide resolved
rewardsPerSecondLimit = _rewardsPerSecondLimit;
minStakingDepositLimit = _minStakingDepositLimit;
timeForEmissionsLimit = _timeForEmissionsLimit;
numServicesLimit = _numServicesLimit;
emissionsLimit = _rewardsPerSecondLimit * _timeForEmissionsLimit * _numServicesLimit;
apyLimit = _apyLimit;

emit StakingLimitsUpdated(_rewardsPerSecondLimit, _timeForEmissionsLimit, _numServicesLimit, emissionsLimit);
emit StakingLimitsUpdated(_minStakingDepositLimit, _timeForEmissionsLimit, _numServicesLimit, _apyLimit);
}

/// @dev Gets emissions amount limit for a specific staking proxy instance.
/// @notice The address field is reserved for the proxy instance, if needed in the next verifier version.
/// @return Emissions amount limit.
function getEmissionsAmountLimit(address) external view returns (uint256) {
return emissionsLimit;
/// @param instance Staking proxy instance.
/// @return amount Emissions amount limit.
function getEmissionsAmountLimit(address instance) external view returns (uint256 amount) {
// Get calculated emissions amount from the instance
amount = IStaking(instance).emissionsAmount();
}
}
8 changes: 7 additions & 1 deletion contracts/test/MockStaking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,19 @@ contract MockStaking {
uint256 public timeForEmissions;
uint256 public maxNumServices;
uint256 public emissionsAmount;
uint256 public minStakingDeposit;
address public token;
address public serviceRegistry;
address public serviceRegistryTokenUtility;
bool public nativeToken;

function initialize(address _token, address _serviceRegistry, address _serviceRegistryTokenUtility) external {
serviceId = 2;
rewardsPerSecond = 0.0001 ether;
rewardsPerSecond = 0.00001 ether;
timeForEmissions = 100;
maxNumServices = 10;
emissionsAmount = rewardsPerSecond * maxNumServices * timeForEmissions;
minStakingDeposit = 10 ether;
token = _token;
serviceRegistry = _serviceRegistry;
if (_serviceRegistryTokenUtility == address(0)) {
Expand Down Expand Up @@ -49,6 +51,10 @@ contract MockStaking {
emissionsAmount = rewardsPerSecond * maxNumServices * timeForEmissions;
}

function setStakingDeposit(uint256 stakingDeposit) external {
minStakingDeposit = stakingDeposit;
}

function stake(uint256) external {}

function unstake(uint256) external {}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"chai": "^4.3.10",
"eslint": "^8.52.0",
"ethers": "^5.7.2",
"hardhat": "^2.22.4",
"hardhat": "^2.22.6",
"hardhat-contract-sizer": "^2.10.0",
"hardhat-deploy": "^0.11.43",
"hardhat-deploy-ethers": "^0.3.0-beta.13",
Expand Down
Loading