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

release v 1.1 #67

Open
wants to merge 371 commits into
base: master
Choose a base branch
from
Open

release v 1.1 #67

wants to merge 371 commits into from

Conversation

bulbozaur
Copy link
Contributor

@bulbozaur bulbozaur commented Apr 30, 2024

Overview

This pull request introduces features for payments and node operators registry management.

Generalized payments with limit

Plug-in factories for Easy Track to be used by the Lido DAO committees with intrusive on-chain enforcement of periodically spendable limits.
Contracts based on the existing RewardPrograms set (RewardProgramsRegistry, AddRewardProgram, RemoveRewardProgram, TopUpRewardProgram) have the following differences from the existing ones:

  • The term "RewardProgram" was substituted for a more general "AllowedRecipient",
  • AllowedRecipientsRegistry (ex-RewardProgramsRegistry) stores and manages limits,
  • TopUpAllowedRecipients (ex-TopUpRewardProgram) can check if the payment satisfies the balance for the current period on motion creating and enaction.

AllowedRecipientsBuilder - provide ability to deploy payments setup with checks
AllowedRecipientsFactory - Factory for pauments related contracts
AllowedRecipientsRegistry - Registry of recipients and limits
AddAllowedRecipient - creates EVMScript to add recipient from registry
RemoveAllowedRecipient - creates EVMScript to remove recipient from registry
TopUpAllowedRecipients - creates EVMScript to transfer ERC-20 token to recipient
LimitsChecker - stores limits params and provides limit-enforcement logic
ADR LIP-18 Audit

Factories set for node operators registry management

This bench of EasyTrack EVM factories to manage an instance of the Simple DVT NodeOperatorsRegistry. The high-view overview of the management flow is presented in the below diagram.
image
ActivateNodeOperators - creates EVMScript to activate several node operators
AddNodeOperators - creates EVMScript to add a new batch of node operators
DeactivateNodeOperators - creates EVMScript to deactivate several node operators
ChangeNodeOperatorManagers - creates EVMScript to change signing keys manager for several node operators
IncreaseVettedValidatorsLimit - creates EVMScript to increase the staking limit for a node operator
SetNodeOperatorNames - creates EVMScript to set the name of several node operators
SetNodeOperatorRewardAddresses - creates EVMScript to set the reward address of several node operators
SetVettedValidatorsLimits - creates EVMScript to set the staking limit for node operators
UpdateTargetValidatorLimits - creates EVMScript to set the node operator's target validators limit
Specification Audit

Utils

  • Holesky testnet support
  • Slither checker for CI
  • Bash bytecode verificator
  • Bytecode diff checker
  • Dependencies update
  • Black linter

zuzueeka and others added 30 commits October 7, 2022 16:38
…ient-tests

feat: add tests for add/remove allowed recipient factories
…gistry-tests

feat: add tests for allowed recipient registry
@bulbozaur bulbozaur requested a review from Psirex April 30, 2024 16:02
@bulbozaur bulbozaur requested a review from rkolpakov May 7, 2024 09:59
Comment on lines +98 to +138
function _validateInputData(SetNameInput[] memory _decodedCallData) private view {
uint256 maxNameLength = nodeOperatorsRegistry.MAX_NODE_OPERATOR_NAME_LENGTH();
uint256 nodeOperatorsCount = nodeOperatorsRegistry.getNodeOperatorsCount();

require(_decodedCallData.length > 0, ERROR_EMPTY_CALLDATA);
require(
_decodedCallData[_decodedCallData.length - 1].nodeOperatorId < nodeOperatorsCount,
ERROR_NODE_OPERATOR_INDEX_OUT_OF_RANGE
);

for (uint256 i = 0; i < _decodedCallData.length; ++i) {
require(
i == 0 ||
_decodedCallData[i].nodeOperatorId > _decodedCallData[i - 1].nodeOperatorId,
ERROR_NODE_OPERATORS_IS_NOT_SORTED
);
require(
bytes(_decodedCallData[i].name).length > 0 &&
bytes(_decodedCallData[i].name).length <= maxNameLength,
ERROR_WRONG_NAME_LENGTH
);

(
/* bool active */,
string memory name,
/* address rewardAddress */,
/* uint64 stakingLimit */,
/* uint64 stoppedValidators */,
/* uint64 totalSigningKeys */,
/* uint64 usedSigningKeys */
) = nodeOperatorsRegistry.getNodeOperator(
_decodedCallData[i].nodeOperatorId,
true
);

require(
keccak256(bytes(_decodedCallData[i].name)) != keccak256(bytes(name)),
ERROR_SAME_NAME
);
}
}
Comment on lines +104 to +137
function _validateInputData(SetRewardAddressInput[] memory _decodedCallData) private view {
uint256 nodeOperatorsCount = nodeOperatorsRegistry.getNodeOperatorsCount();

require(_decodedCallData.length > 0, ERROR_EMPTY_CALLDATA);
require(
_decodedCallData[_decodedCallData.length - 1].nodeOperatorId < nodeOperatorsCount,
ERROR_NODE_OPERATOR_INDEX_OUT_OF_RANGE
);

for (uint256 i = 0; i < _decodedCallData.length; ++i) {
require(
i == 0 ||
_decodedCallData[i].nodeOperatorId > _decodedCallData[i - 1].nodeOperatorId,
ERROR_NODE_OPERATORS_IS_NOT_SORTED
);
require(_decodedCallData[i].rewardAddress != lido, ERROR_LIDO_REWARD_ADDRESS);
require(_decodedCallData[i].rewardAddress != address(0), ERROR_ZERO_REWARD_ADDRESS);

(
/* bool active */,
/* string memory name */,
address rewardAddress,
/* uint64 stakingLimit */,
/* uint64 stoppedValidators */,
/* uint64 totalSigningKeys */,
/* uint64 usedSigningKeys */
) = nodeOperatorsRegistry.getNodeOperator(
_decodedCallData[i].nodeOperatorId,
false
);

require(_decodedCallData[i].rewardAddress != rewardAddress, ERROR_SAME_REWARD_ADDRESS);
}
}
Comment on lines +128 to +146
function _getNodeOperatorData(
uint256 _nodeOperatorId
) private view returns (NodeOperatorData memory _nodeOperatorData) {
(
bool active,
,
address rewardAddress,
uint64 stakingLimit,
,
uint64 totalSigningKeys,

) = nodeOperatorsRegistry.getNodeOperator(_nodeOperatorId, false);

_nodeOperatorData.id = _nodeOperatorId;
_nodeOperatorData.active = active;
_nodeOperatorData.rewardAddress = rewardAddress;
_nodeOperatorData.stakingLimit = stakingLimit;
_nodeOperatorData.totalSigningKeys = totalSigningKeys;
}
Comment on lines +299 to +315
function _getPeriodStartFromTimestamp(uint256 _timestamp) internal view returns (uint256) {
// Get year and number of month of the timestamp:
(uint256 year, uint256 month, ) = bokkyPooBahsDateTimeContract.timestampToDate(_timestamp);
// We assume that the year will remain the same,
// because the beginning of the current calendar period will necessarily be in the same year.
uint256 periodStartYear = year;
// Get the number of the start date month:
uint256 periodStartMonth = _getFirstMonthInPeriodFromMonth(month, periodDurationMonths);
// The beginning of the period always matches the calendar date of the beginning of the month.
uint256 periodStartDay = 1;
return
bokkyPooBahsDateTimeContract.timestampFromDate(
periodStartYear,
periodStartMonth,
periodStartDay
);
}

Check warning

Code scanning / Slither

Unused return Medium

Comment on lines +100 to +138
function _validateInputData(VettedValidatorsLimitInput[] memory _decodedCallData) private view {
uint256 nodeOperatorsCount = nodeOperatorsRegistry.getNodeOperatorsCount();
require(_decodedCallData.length > 0, ERROR_EMPTY_CALLDATA);
require(
_decodedCallData[_decodedCallData.length - 1].nodeOperatorId < nodeOperatorsCount,
ERROR_NODE_OPERATOR_INDEX_OUT_OF_RANGE
);

for (uint256 i = 0; i < _decodedCallData.length; ++i) {
require(
i == 0 ||
_decodedCallData[i].nodeOperatorId > _decodedCallData[i - 1].nodeOperatorId,
ERROR_NODE_OPERATORS_IS_NOT_SORTED
);

(
bool active,
/* string memory name */,
/* address rewardAddress */,
/* uint64 stakingLimit */,
/* uint64 stoppedValidators */,
uint64 totalSigningKeys,
/* uint64 usedSigningKeys */
) = nodeOperatorsRegistry.getNodeOperator(
_decodedCallData[i].nodeOperatorId,
false
);

require(
totalSigningKeys >= _decodedCallData[i].stakingLimit,
ERROR_NOT_ENOUGH_SIGNING_KEYS
);

require(
active == true,
ERROR_NODE_OPERATOR_IS_NOT_ACTIVE
);
}
}
address public token;

/// @notice Address of AllowedRecipientsRegistry contract
AllowedRecipientsRegistry public allowedRecipientsRegistry;

Check warning

Code scanning / Slither

State variables that could be declared immutable Warning

// -------------

/// @notice Address of AllowedRecipientsRegistry
AllowedRecipientsRegistry public allowedRecipientsRegistry;

Check warning

Code scanning / Slither

State variables that could be declared immutable Warning

IFinance public immutable finance;

/// @notice Address of payout token
address public token;

Check warning

Code scanning / Slither

State variables that could be declared immutable Warning

TopUpAllowedRecipients.token should be immutable
// -------------

/// @notice Address of AllowedRecipientsRegistry
AllowedRecipientsRegistry public allowedRecipientsRegistry;

Check warning

Code scanning / Slither

State variables that could be declared immutable Warning

Copy link

@rkolpakov rkolpakov left a comment

Choose a reason for hiding this comment

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

LGTM. Good job!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants