diff --git a/contracts/sfc/ConstantsManager.sol b/contracts/sfc/ConstantsManager.sol index a2a1676..47e2be1 100644 --- a/contracts/sfc/ConstantsManager.sol +++ b/contracts/sfc/ConstantsManager.sol @@ -26,8 +26,6 @@ contract ConstantsManager is Ownable { uint256 public baseRewardPerSecond; uint256 public offlinePenaltyThresholdBlocksNum; uint256 public offlinePenaltyThresholdTime; - uint256 public targetGasPowerPerSecond; - uint256 public gasPriceBalancingCounterweight; // The number of epochs to calculate the average uptime ratio from, acceptable bound [10, 87600]. // Is also the minimum number of epochs necessary for deactivation of offline validators. @@ -81,14 +79,14 @@ contract ConstantsManager is Ownable { } function updateBurntFeeShare(uint256 v) external virtual onlyOwner { - if (v > Decimal.unit() / 2) { + if (v + treasuryFeeShare > Decimal.unit()) { revert ValueTooLarge(); } burntFeeShare = v; } function updateTreasuryFeeShare(uint256 v) external virtual onlyOwner { - if (v > Decimal.unit() / 2) { + if (v + burntFeeShare > Decimal.unit()) { revert ValueTooLarge(); } treasuryFeeShare = v; @@ -115,9 +113,6 @@ contract ConstantsManager is Ownable { } function updateBaseRewardPerSecond(uint256 v) external virtual onlyOwner { - if (v < Decimal.unit() / 2) { - revert ValueTooSmall(); - } if (v > 32 * Decimal.unit()) { revert ValueTooLarge(); } @@ -144,26 +139,6 @@ contract ConstantsManager is Ownable { offlinePenaltyThresholdBlocksNum = v; } - function updateTargetGasPowerPerSecond(uint256 v) external virtual onlyOwner { - if (v < 1000000) { - revert ValueTooSmall(); - } - if (v > 500000000) { - revert ValueTooLarge(); - } - targetGasPowerPerSecond = v; - } - - function updateGasPriceBalancingCounterweight(uint256 v) external virtual onlyOwner { - if (v < 100) { - revert ValueTooSmall(); - } - if (v > 10 * 86400) { - revert ValueTooLarge(); - } - gasPriceBalancingCounterweight = v; - } - function updateAverageUptimeEpochWindow(uint32 v) external virtual onlyOwner { if (v < 10) { // needs to be long enough to allow permissible downtime for validators maintenance diff --git a/contracts/sfc/NetworkInitializer.sol b/contracts/sfc/NetworkInitializer.sol index a738276..2ee60f8 100644 --- a/contracts/sfc/NetworkInitializer.sol +++ b/contracts/sfc/NetworkInitializer.sol @@ -25,18 +25,16 @@ contract NetworkInitializer { NodeDriverAuth(_auth).initialize(_sfc, _driver, _owner); ConstantsManager consts = new ConstantsManager(address(this)); - consts.updateMinSelfStake(500000 * Decimal.unit()); + consts.updateMinSelfStake(500_000 * Decimal.unit()); consts.updateMaxDelegatedRatio(16 * Decimal.unit()); consts.updateValidatorCommission((15 * Decimal.unit()) / 100); - consts.updateBurntFeeShare((20 * Decimal.unit()) / 100); - consts.updateTreasuryFeeShare((10 * Decimal.unit()) / 100); + consts.updateBurntFeeShare(0); + consts.updateTreasuryFeeShare((90 * Decimal.unit()) / 100); consts.updateWithdrawalPeriodEpochs(3); consts.updateWithdrawalPeriodTime(60 * 60 * 24 * 7); - consts.updateBaseRewardPerSecond(2668658453701531600); + consts.updateBaseRewardPerSecond(1_000); consts.updateOfflinePenaltyThresholdTime(5 days); - consts.updateOfflinePenaltyThresholdBlocksNum(1000); - consts.updateTargetGasPowerPerSecond(2000000); - consts.updateGasPriceBalancingCounterweight(3600); + consts.updateOfflinePenaltyThresholdBlocksNum(1_000); consts.updateAverageUptimeEpochWindow(100); consts.updateMinAverageUptime(0); // check disabled by default consts.transferOwnership(_owner); diff --git a/contracts/test/UnitTestConstantsManager.sol b/contracts/test/UnitTestConstantsManager.sol index 4ba2327..6cde2e0 100644 --- a/contracts/test/UnitTestConstantsManager.sol +++ b/contracts/test/UnitTestConstantsManager.sol @@ -14,15 +14,7 @@ contract UnitTestConstantsManager is ConstantsManager { baseRewardPerSecond = v; } - function updateGasPriceBalancingCounterweight(uint256 v) external override onlyOwner { - gasPriceBalancingCounterweight = v; - } - function updateOfflinePenaltyThresholdTime(uint256 v) external override onlyOwner { offlinePenaltyThresholdTime = v; } - - function updateTargetGasPowerPerSecond(uint256 v) external override onlyOwner { - targetGasPowerPerSecond = v; - } } diff --git a/contracts/test/UnitTestSFC.sol b/contracts/test/UnitTestSFC.sol index 112a2d4..a589d1a 100644 --- a/contracts/test/UnitTestSFC.sol +++ b/contracts/test/UnitTestSFC.sol @@ -76,8 +76,6 @@ contract UnitTestNetworkInitializer { consts.updateBaseRewardPerSecond(6183414351851851852); consts.updateOfflinePenaltyThresholdTime(3 days); consts.updateOfflinePenaltyThresholdBlocksNum(1000); - consts.updateTargetGasPowerPerSecond(2000000); - consts.updateGasPriceBalancingCounterweight(6 * 60 * 60); consts.updateAverageUptimeEpochWindow(10); consts.updateMinAverageUptime(0); // check disabled by default consts.transferOwnership(_owner); diff --git a/test/ConstantsManager.ts b/test/ConstantsManager.ts new file mode 100644 index 0000000..50e2d61 --- /dev/null +++ b/test/ConstantsManager.ts @@ -0,0 +1,335 @@ +import { ethers } from 'hardhat'; +import { expect } from 'chai'; +import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; +import { ConstantsManager } from '../typechain-types'; + +describe('ConstantsManager', () => { + const fixture = async () => { + const [owner, nonOwner] = await ethers.getSigners(); + const manager: ConstantsManager = await ethers.deployContract('ConstantsManager', [owner]); + + return { + owner, + nonOwner, + manager, + }; + }; + + beforeEach(async function () { + Object.assign(this, await loadFixture(fixture)); + }); + + describe('Update min self-stake', () => { + it('Should revert when not owner', async function () { + await expect(this.manager.connect(this.nonOwner).updateMinSelfStake(1000)).to.be.revertedWithCustomError( + this.manager, + 'OwnableUnauthorizedAccount', + ); + }); + + it('Should revert when value is too small', async function () { + await expect( + this.manager.connect(this.owner).updateMinSelfStake(100_000n * BigInt(1e18) - 1n), + ).to.be.revertedWithCustomError(this.manager, 'ValueTooSmall'); + }); + + it('Should revert when value is too large', async function () { + await expect( + this.manager.connect(this.owner).updateMinSelfStake(10_000_000n * BigInt(1e18) + 1n), + ).to.be.revertedWithCustomError(this.manager, 'ValueTooLarge'); + }); + + it('Should succeed and update min self-stake', async function () { + const newValue = 1_000_000n * BigInt(1e18); + await this.manager.connect(this.owner).updateMinSelfStake(newValue); + expect(await this.manager.minSelfStake()).to.equal(newValue); + }); + }); + + describe('Update max delegated ratio', () => { + it('Should revert when not owner', async function () { + await expect(this.manager.connect(this.nonOwner).updateMaxDelegatedRatio(1000)).to.be.revertedWithCustomError( + this.manager, + 'OwnableUnauthorizedAccount', + ); + }); + + it('Should revert when value is too small', async function () { + await expect( + this.manager.connect(this.owner).updateMaxDelegatedRatio(BigInt(1e18) - 1n), + ).to.be.revertedWithCustomError(this.manager, 'ValueTooSmall'); + }); + + it('Should revert when value is too large', async function () { + await expect( + this.manager.connect(this.owner).updateMaxDelegatedRatio(31n * BigInt(1e18) + 1n), + ).to.be.revertedWithCustomError(this.manager, 'ValueTooLarge'); + }); + + it('Should succeed and update max delegated ratio', async function () { + const newValue = BigInt(1e18); + await this.manager.connect(this.owner).updateMaxDelegatedRatio(newValue); + expect(await this.manager.maxDelegatedRatio()).to.equal(newValue); + }); + }); + + describe('Update validator commission', () => { + it('Should revert when not owner', async function () { + await expect(this.manager.connect(this.nonOwner).updateValidatorCommission(1000)).to.be.revertedWithCustomError( + this.manager, + 'OwnableUnauthorizedAccount', + ); + }); + + it('Should revert when value is too large', async function () { + await expect( + this.manager.connect(this.owner).updateValidatorCommission(BigInt(1e18) / 2n + 1n), + ).to.be.revertedWithCustomError(this.manager, 'ValueTooLarge'); + }); + + it('Should succeed and update validator commission', async function () { + const newValue = BigInt(1e18) / 2n; + await this.manager.connect(this.owner).updateValidatorCommission(newValue); + expect(await this.manager.validatorCommission()).to.equal(newValue); + }); + }); + + describe('Update burnt fee share', () => { + it('Should revert when not owner', async function () { + await expect(this.manager.connect(this.nonOwner).updateBurntFeeShare(1000)).to.be.revertedWithCustomError( + this.manager, + 'OwnableUnauthorizedAccount', + ); + }); + + it('Should revert when value is too large', async function () { + // set treasury fee share to 60% + await this.manager.connect(this.owner).updateTreasuryFeeShare((BigInt(1e18) * 60n) / 100n); + + // set burnt fee share to 50% -> should revert because exceeds 100% + await expect( + this.manager.connect(this.owner).updateBurntFeeShare((BigInt(1e18) * 50n) / 100n), + ).to.be.revertedWithCustomError(this.manager, 'ValueTooLarge'); + }); + + it('Should succeed and update burnt fee share', async function () { + const newValue = BigInt(1e18) / 2n; + await this.manager.connect(this.owner).updateBurntFeeShare(newValue); + expect(await this.manager.burntFeeShare()).to.equal(newValue); + }); + }); + + describe('Update treasury fee share', () => { + it('Should revert when not owner', async function () { + await expect(this.manager.connect(this.nonOwner).updateTreasuryFeeShare(1000)).to.be.revertedWithCustomError( + this.manager, + 'OwnableUnauthorizedAccount', + ); + }); + + it('Should revert when value is too large', async function () { + // set burnt fee share to 40% + await this.manager.connect(this.owner).updateBurntFeeShare((BigInt(1e18) * 40n) / 100n); + + // set treasury fee share to 61% -> should revert because exceeds 100% + await expect( + this.manager.connect(this.owner).updateTreasuryFeeShare((BigInt(1e18) * 61n) / 100n), + ).to.be.revertedWithCustomError(this.manager, 'ValueTooLarge'); + }); + + it('Should succeed and update treasury fee share', async function () { + const newValue = BigInt(1e18) / 2n; + await this.manager.connect(this.owner).updateTreasuryFeeShare(newValue); + expect(await this.manager.treasuryFeeShare()).to.equal(newValue); + }); + }); + + describe('Update withdrawal period epochs', () => { + it('Should revert when not owner', async function () { + await expect( + this.manager.connect(this.nonOwner).updateWithdrawalPeriodEpochs(1000), + ).to.be.revertedWithCustomError(this.manager, 'OwnableUnauthorizedAccount'); + }); + + it('Should revert when value is too small', async function () { + await expect(this.manager.connect(this.owner).updateWithdrawalPeriodEpochs(1)).to.be.revertedWithCustomError( + this.manager, + 'ValueTooSmall', + ); + }); + + it('Should revert when value is too large', async function () { + await expect(this.manager.connect(this.owner).updateWithdrawalPeriodEpochs(101)).to.be.revertedWithCustomError( + this.manager, + 'ValueTooLarge', + ); + }); + + it('Should succeed and update withdrawal period epochs', async function () { + const newValue = 50; + await this.manager.connect(this.owner).updateWithdrawalPeriodEpochs(newValue); + expect(await this.manager.withdrawalPeriodEpochs()).to.equal(newValue); + }); + }); + + describe('Update withdrawal period time', () => { + it('Should revert when not owner', async function () { + await expect(this.manager.connect(this.nonOwner).updateWithdrawalPeriodTime(1000)).to.be.revertedWithCustomError( + this.manager, + 'OwnableUnauthorizedAccount', + ); + }); + + it('Should revert when value is too small', async function () { + await expect( + this.manager.connect(this.owner).updateWithdrawalPeriodTime(86_400 - 1), + ).to.be.revertedWithCustomError(this.manager, 'ValueTooSmall'); + }); + + it('Should revert when value is too large', async function () { + await expect( + this.manager.connect(this.owner).updateWithdrawalPeriodTime(30 * 86_400 + 1), + ).to.be.revertedWithCustomError(this.manager, 'ValueTooLarge'); + }); + + it('Should succeed and update withdrawal period time', async function () { + const newValue = 86_400; + await this.manager.connect(this.owner).updateWithdrawalPeriodTime(newValue); + expect(await this.manager.withdrawalPeriodTime()).to.equal(newValue); + }); + }); + + describe('Update base reward per second', () => { + it('Should revert when not owner', async function () { + await expect(this.manager.connect(this.nonOwner).updateBaseRewardPerSecond(1000)).to.be.revertedWithCustomError( + this.manager, + 'OwnableUnauthorizedAccount', + ); + }); + + it('Should revert when value is too large', async function () { + await expect( + this.manager.connect(this.owner).updateBaseRewardPerSecond(32n * BigInt(1e18) + 1n), + ).to.be.revertedWithCustomError(this.manager, 'ValueTooLarge'); + }); + + it('Should succeed and update base reward per second', async function () { + const newValue = BigInt(1e18); + await this.manager.connect(this.owner).updateBaseRewardPerSecond(newValue); + expect(await this.manager.baseRewardPerSecond()).to.equal(newValue); + }); + }); + + describe('Update offline penalty threshold time', () => { + it('Should revert when not owner', async function () { + await expect( + this.manager.connect(this.nonOwner).updateOfflinePenaltyThresholdTime(1000), + ).to.be.revertedWithCustomError(this.manager, 'OwnableUnauthorizedAccount'); + }); + + it('Should revert when value is too small', async function () { + await expect( + this.manager.connect(this.owner).updateOfflinePenaltyThresholdTime(86_400 - 1), + ).to.be.revertedWithCustomError(this.manager, 'ValueTooSmall'); + }); + + it('Should revert when value is too large', async function () { + await expect( + this.manager.connect(this.owner).updateOfflinePenaltyThresholdTime(10 * 86_400 + 1), + ).to.be.revertedWithCustomError(this.manager, 'ValueTooLarge'); + }); + + it('Should succeed and update offline penalty threshold time', async function () { + const newValue = 86_400; + await this.manager.connect(this.owner).updateOfflinePenaltyThresholdTime(newValue); + expect(await this.manager.offlinePenaltyThresholdTime()).to.equal(newValue); + }); + }); + + describe('Update offline penalty threshold blocks num', () => { + it('Should revert when not owner', async function () { + await expect( + this.manager.connect(this.nonOwner).updateOfflinePenaltyThresholdBlocksNum(1000), + ).to.be.revertedWithCustomError(this.manager, 'OwnableUnauthorizedAccount'); + }); + + it('Should revert when value is too small', async function () { + await expect( + this.manager.connect(this.owner).updateOfflinePenaltyThresholdBlocksNum(99), + ).to.be.revertedWithCustomError(this.manager, 'ValueTooSmall'); + }); + + it('Should revert when value is too large', async function () { + await expect( + this.manager.connect(this.owner).updateOfflinePenaltyThresholdBlocksNum(1_000_001), + ).to.be.revertedWithCustomError(this.manager, 'ValueTooLarge'); + }); + + it('Should succeed and update offline penalty threshold blocks num', async function () { + const newValue = 500; + await this.manager.connect(this.owner).updateOfflinePenaltyThresholdBlocksNum(newValue); + expect(await this.manager.offlinePenaltyThresholdBlocksNum()).to.equal(newValue); + }); + }); + + describe('Update average uptime epoch window', () => { + it('Should revert when not owner', async function () { + await expect( + this.manager.connect(this.nonOwner).updateAverageUptimeEpochWindow(1000), + ).to.be.revertedWithCustomError(this.manager, 'OwnableUnauthorizedAccount'); + }); + + it('Should revert when value is too small', async function () { + await expect(this.manager.connect(this.owner).updateAverageUptimeEpochWindow(9)).to.be.revertedWithCustomError( + this.manager, + 'ValueTooSmall', + ); + }); + + it('Should revert when value is too large', async function () { + await expect( + this.manager.connect(this.owner).updateAverageUptimeEpochWindow(87_601), + ).to.be.revertedWithCustomError(this.manager, 'ValueTooLarge'); + }); + + it('Should succeed and update average uptime epoch window', async function () { + const newValue = 50; + await this.manager.connect(this.owner).updateAverageUptimeEpochWindow(newValue); + expect(await this.manager.averageUptimeEpochWindow()).to.equal(newValue); + }); + }); + + describe('Update min average uptime', () => { + it('Should revert when not owner', async function () { + await expect(this.manager.connect(this.nonOwner).updateMinAverageUptime(1000)).to.be.revertedWithCustomError( + this.manager, + 'OwnableUnauthorizedAccount', + ); + }); + + it('Should revert when value is too large', async function () { + await expect( + this.manager.connect(this.owner).updateMinAverageUptime((BigInt(1e18) * 9n) / 10n + 1n), + ).to.be.revertedWithCustomError(this.manager, 'ValueTooLarge'); + }); + + it('Should succeed and update min average uptime', async function () { + const newValue = 95; + await this.manager.connect(this.owner).updateMinAverageUptime(newValue); + expect(await this.manager.minAverageUptime()).to.equal(newValue); + }); + }); + + describe('Update issued tokens recipient', () => { + it('Should revert when not owner', async function () { + await expect( + this.manager.connect(this.nonOwner).updateIssuedTokensRecipient(this.nonOwner.address), + ).to.be.revertedWithCustomError(this.manager, 'OwnableUnauthorizedAccount'); + }); + + it('Should succeed and update issued tokens recipient', async function () { + await this.manager.connect(this.owner).updateIssuedTokensRecipient(this.nonOwner); + expect(await this.manager.issuedTokensRecipient()).to.equal(this.nonOwner); + }); + }); +});