From 4f38101d106bace102a1bd45afe04ea0c5add4b3 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Fri, 2 Feb 2024 13:36:59 +0100 Subject: [PATCH] Added Stake balancing for score calculations, added possibility to once set the delay free period for operator fees --- abi/NodeOperatorFeeChangesStorage.json | 26 +++++++++++++++++++ contracts/v1/Profile.sol | 2 +- contracts/v1/scoring/log2pldsf.sol | 9 ++++--- contracts/v1/storage/ParametersStorage.sol | 2 +- contracts/v2/Staking.sol | 8 ++++-- contracts/v2/scoring/LinearSum.sol | 3 ++- .../storage/NodeOperatorFeeChangesStorage.sol | 13 ++++++++++ test/v1/unit/ParametersStorage.test.ts | 4 +-- test/v1/unit/Profile.test.ts | 4 +-- 9 files changed, 59 insertions(+), 12 deletions(-) diff --git a/abi/NodeOperatorFeeChangesStorage.json b/abi/NodeOperatorFeeChangesStorage.json index cacf15fa..9933535b 100644 --- a/abi/NodeOperatorFeeChangesStorage.json +++ b/abi/NodeOperatorFeeChangesStorage.json @@ -33,6 +33,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "delayFreePeriodEnd", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -153,6 +166,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "name": "setDelayFreePeriodEnd", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [], "name": "version", diff --git a/contracts/v1/Profile.sol b/contracts/v1/Profile.sol index 1b5f321d..0ec0ae10 100644 --- a/contracts/v1/Profile.sol +++ b/contracts/v1/Profile.sol @@ -23,7 +23,7 @@ contract Profile is Named, Versioned, ContractStatus, Initializable { event AskUpdated(uint72 indexed identityId, bytes nodeId, uint96 ask); string private constant _NAME = "Profile"; - string private constant _VERSION = "1.0.2"; + string private constant _VERSION = "1.0.3"; HashingProxy public hashingProxy; Identity public identityContract; diff --git a/contracts/v1/scoring/log2pldsf.sol b/contracts/v1/scoring/log2pldsf.sol index d014a417..5efe9dff 100644 --- a/contracts/v1/scoring/log2pldsf.sol +++ b/contracts/v1/scoring/log2pldsf.sol @@ -63,10 +63,13 @@ contract Log2PLDSF is IScoreFunction, Indexable, Named, HubDependent, Initializa } function calculateScore(uint256 distance, uint96 stake) external view returns (uint40) { - uint256 mappedDistance = distance / distanceMappingCoefficient; - uint96 mappedStake = stake / (parametersStorage.maximumStake() / stakeRangeMax); + uint64 coefficient = 1e18; + uint96 maxStake = parametersStorage.maximumStake(); + + uint96 balancedStake = stake <= maxStake ? stake : maxStake; + uint96 mappedStake = balancedStake / (maxStake / stakeRangeMax); - uint64 coefficient = 1 ether; + uint256 mappedDistance = distance / distanceMappingCoefficient; return uint40( diff --git a/contracts/v1/storage/ParametersStorage.sol b/contracts/v1/storage/ParametersStorage.sol index dee76b96..a68fa2fc 100644 --- a/contracts/v1/storage/ParametersStorage.sol +++ b/contracts/v1/storage/ParametersStorage.sol @@ -10,7 +10,7 @@ contract ParametersStorage is Named, Versioned, HubDependent { event ParameterChanged(string parameterName, uint256 parameterValue); string private constant _NAME = "ParametersStorage"; - string private constant _VERSION = "1.1.0"; + string private constant _VERSION = "1.1.1"; // 0 - minProofWindowOffsetPerc // 1 - maxProofWindowOffsetPerc diff --git a/contracts/v2/Staking.sol b/contracts/v2/Staking.sol index b57b8588..c7d1a3c0 100644 --- a/contracts/v2/Staking.sol +++ b/contracts/v2/Staking.sol @@ -228,8 +228,12 @@ contract StakingV2 is Named, Versioned, ContractStatus, Initializable { function startOperatorFeeChange(uint72 identityId, uint8 newOperatorFee) external onlyAdmin(identityId) { if (newOperatorFee > 100) revert StakingErrors.InvalidOperatorFee(); - uint256 feeChangeDelayEnd = block.timestamp + parametersStorage.stakeWithdrawalDelay(); - nodeOperatorFeeChangesStorage.createOperatorFeeChangeRequest(identityId, newOperatorFee, feeChangeDelayEnd); + NodeOperatorFeeChangesStorage nofcs = nodeOperatorFeeChangesStorage; + + uint256 feeChangeDelayEnd = block.timestamp > nofcs.delayFreePeriodEnd() + ? block.timestamp + parametersStorage.stakeWithdrawalDelay() + : block.timestamp; + nofcs.createOperatorFeeChangeRequest(identityId, newOperatorFee, feeChangeDelayEnd); emit OperatorFeeChangeStarted( identityId, diff --git a/contracts/v2/scoring/LinearSum.sol b/contracts/v2/scoring/LinearSum.sol index 6f74785d..e7c52a80 100644 --- a/contracts/v2/scoring/LinearSum.sol +++ b/contracts/v2/scoring/LinearSum.sol @@ -128,8 +128,9 @@ contract LinearSum is IProximityScoreFunctionsPair, Indexable, Named, HubDepende uint96 minStake = ps.minimumStake(); uint96 maxStake = ps.maximumStake(); + uint96 balancedStake = stake <= maxStake ? stake : maxStake; - return uint64((uint256(stakeScaleFactor) * (stake - minStake)) / (maxStake - minStake)); + return uint64((uint256(stakeScaleFactor) * (balancedStake - minStake)) / (maxStake - minStake)); } function getParameters() external view returns (uint96, uint96, uint32, uint32) { diff --git a/contracts/v2/storage/NodeOperatorFeeChangesStorage.sol b/contracts/v2/storage/NodeOperatorFeeChangesStorage.sol index a56683da..4aa83479 100644 --- a/contracts/v2/storage/NodeOperatorFeeChangesStorage.sol +++ b/contracts/v2/storage/NodeOperatorFeeChangesStorage.sol @@ -15,12 +15,21 @@ contract NodeOperatorFeeChangesStorage is Named, Versioned, HubDependent { uint256 timestamp; } + bool private _delayFreePeriodSet; + uint256 public delayFreePeriodEnd; + // identityId => operatorFeeChangeRequest mapping(uint72 => OperatorFeeChangeRequest) public operatorFeeChangeRequests; // solhint-disable-next-line no-empty-blocks constructor(address hubAddress) HubDependent(hubAddress) {} + modifier onlyOnce() { + require(!_delayFreePeriodSet, "Function has already been executed."); + _; + _delayFreePeriodSet = true; + } + function name() external pure virtual override returns (string memory) { return _NAME; } @@ -48,4 +57,8 @@ contract NodeOperatorFeeChangesStorage is Named, Versioned, HubDependent { function operatorFeeChangeRequestExists(uint72 identityId) external view returns (bool) { return operatorFeeChangeRequests[identityId].timestamp != 0; } + + function setDelayFreePeriodEnd(uint256 timestamp) external onlyHubOwner onlyOnce { + delayFreePeriodEnd = timestamp; + } } diff --git a/test/v1/unit/ParametersStorage.test.ts b/test/v1/unit/ParametersStorage.test.ts index 3546d0ca..f0d7e17a 100644 --- a/test/v1/unit/ParametersStorage.test.ts +++ b/test/v1/unit/ParametersStorage.test.ts @@ -310,7 +310,7 @@ describe('@v1 @unit ParametersStorage contract', function () { }); it('validate stake withdrawal delay for owner, expect to pass', async () => { - const valueInContract = 5; + const valueInContract = 1; const newValue = '7'; stakeWithdrawalDelay = await ParametersStorage.stakeWithdrawalDelay(); const expectedValue = `${stakeWithdrawalDelay}/60`; @@ -336,7 +336,7 @@ describe('@v1 @unit ParametersStorage contract', function () { }); it('validate reward withdrawal delay for owner, expect to pass', async () => { - const valueInContract = 5; + const valueInContract = 1; const newValue = '7'; rewardWithdrawalDelay = await ParametersStorage.rewardWithdrawalDelay(); const expectedValue = `${rewardWithdrawalDelay}/60`; diff --git a/test/v1/unit/Profile.test.ts b/test/v1/unit/Profile.test.ts index 747f8e82..3bed8777 100644 --- a/test/v1/unit/Profile.test.ts +++ b/test/v1/unit/Profile.test.ts @@ -57,8 +57,8 @@ describe('@v1 @unit Profile contract', function () { expect(await Profile.name()).to.equal('Profile'); }); - it('The contract is version "1.0.2"', async () => { - expect(await Profile.version()).to.equal('1.0.2'); + it('The contract is version "1.0.3"', async () => { + expect(await Profile.version()).to.equal('1.0.3'); }); it('Create a profile with whitelisted node, expect to pass', async () => {