diff --git a/contracts/common/Initializable.sol b/contracts/common/Initializable.sol index e7cb09a..97d2ff7 100644 --- a/contracts/common/Initializable.sol +++ b/contracts/common/Initializable.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.9; -import {IErrors} from "../IErrors.sol"; - /** * @title Initializable * @@ -15,7 +13,7 @@ import {IErrors} from "../IErrors.sol"; * a parent initializer twice, or ensure that all initializers are idempotent, * because this is not dealt with automatically as with constructors. */ -contract Initializable is IErrors { +contract Initializable { /** * @dev Indicates that the contract has been initialized. */ @@ -26,12 +24,17 @@ contract Initializable is IErrors { */ bool private initializing; + /** + * @dev The contract instance has already been initialized. + */ + error InvalidInitialization(); + /** * @dev Modifier to use in the initializer function of a contract. */ modifier initializer() { if (!initializing && initialized) { - revert ContractInitialized(); + revert InvalidInitialization(); } bool isTopLevelCall = !initializing; diff --git a/contracts/common/ReentrancyGuard.sol b/contracts/common/ReentrancyGuard.sol index b1359c4..5919a27 100644 --- a/contracts/common/ReentrancyGuard.sol +++ b/contracts/common/ReentrancyGuard.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.9; import {Initializable} from "./Initializable.sol"; -import {IErrors} from "../IErrors.sol"; /** * @dev Contract module that helps prevent reentrant calls to a function. @@ -16,10 +15,15 @@ import {IErrors} from "../IErrors.sol"; * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. */ -contract ReentrancyGuard is IErrors, Initializable { +contract ReentrancyGuard is Initializable { // counter to allow mutex lock with only one SSTORE operation uint256 private _guardCounter; + /** + * @dev Reentrant call. + */ + error ReentrancyGuardReentrantCall(); + function initialize() internal initializer { // The counter starts at one to prevent changing it from zero to a non-zero // value, which is a more expensive operation. @@ -38,7 +42,7 @@ contract ReentrancyGuard is IErrors, Initializable { uint256 localCounter = _guardCounter; _; if (localCounter != _guardCounter) { - revert ReentrantCall(); + revert ReentrancyGuardReentrantCall(); } } diff --git a/contracts/interfaces/IEVMWriter.sol b/contracts/interfaces/IEVMWriter.sol new file mode 100644 index 0000000..575a7b4 --- /dev/null +++ b/contracts/interfaces/IEVMWriter.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.9; + +interface IEvmWriter { + function setBalance(address acc, uint256 value) external; + + function copyCode(address acc, address from) external; + + function swapCode(address acc, address where) external; + + function setStorage(address acc, bytes32 key, bytes32 value) external; + + function incNonce(address acc, uint256 diff) external; +} diff --git a/contracts/ownership/Ownable.sol b/contracts/ownership/Ownable.sol index 157212a..a7292d7 100644 --- a/contracts/ownership/Ownable.sol +++ b/contracts/ownership/Ownable.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.9; import {Initializable} from "../common/Initializable.sol"; -import {IErrors} from "../IErrors.sol"; /** * @dev Contract module which provides a basic access control mechanism, where @@ -13,9 +12,19 @@ import {IErrors} from "../IErrors.sol"; * `onlyOwner`, which can be aplied to your functions to restrict their use to * the owner. */ -contract Ownable is IErrors, Initializable { +contract Ownable is Initializable { address private _owner; + /** + * @dev The caller account is not authorized to perform an operation. + */ + error OwnableUnauthorizedAccount(address account); + + /** + * @dev The owner is not a valid owner account. (eg. `address(0)`) + */ + error OwnableInvalidOwner(address owner); + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** @@ -38,7 +47,7 @@ contract Ownable is IErrors, Initializable { */ modifier onlyOwner() { if (!isOwner()) { - revert NotOwner(); + revert OwnableUnauthorizedAccount(msg.sender); } _; } @@ -75,7 +84,7 @@ contract Ownable is IErrors, Initializable { */ function _transferOwnership(address newOwner) internal { if (newOwner == address(0)) { - revert ZeroAddress(); + revert OwnableInvalidOwner(address(0)); } emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; diff --git a/contracts/sfc/NodeDriver.sol b/contracts/sfc/NodeDriver.sol index 74e0184..c58e44c 100644 --- a/contracts/sfc/NodeDriver.sol +++ b/contracts/sfc/NodeDriver.sol @@ -3,23 +3,14 @@ pragma solidity ^0.8.9; import {Initializable} from "../common/Initializable.sol"; import {NodeDriverAuth} from "./NodeDriverAuth.sol"; -import {IErrors} from "../IErrors.sol"; +import {IEvmWriter} from "../interfaces/IEVMWriter.sol"; -interface EVMWriter { - function setBalance(address acc, uint256 value) external; - - function copyCode(address acc, address from) external; - - function swapCode(address acc, address where) external; - - function setStorage(address acc, bytes32 key, bytes32 value) external; - - function incNonce(address acc, uint256 diff) external; -} - -contract NodeDriver is IErrors, Initializable { +contract NodeDriver is Initializable { NodeDriverAuth internal backend; - EVMWriter internal evmWriter; + IEvmWriter internal evmWriter; + + error NotNode(); + error NotBackend(); event UpdatedBackend(address indexed backend); @@ -45,7 +36,7 @@ contract NodeDriver is IErrors, Initializable { function initialize(address _backend, address _evmWriterAddress) external initializer { backend = NodeDriverAuth(_backend); emit UpdatedBackend(_backend); - evmWriter = EVMWriter(_evmWriterAddress); + evmWriter = IEvmWriter(_evmWriterAddress); } function setBalance(address acc, uint256 value) external onlyBackend { diff --git a/contracts/sfc/NodeDriverAuth.sol b/contracts/sfc/NodeDriverAuth.sol index 73dd5ff..2801fff 100644 --- a/contracts/sfc/NodeDriverAuth.sol +++ b/contracts/sfc/NodeDriverAuth.sol @@ -5,16 +5,22 @@ import {Initializable} from "../common/Initializable.sol"; import {Ownable} from "../ownership/Ownable.sol"; import {SFCI} from "./SFCI.sol"; import {NodeDriver} from "./NodeDriver.sol"; -import {IErrors} from "../IErrors.sol"; interface NodeDriverExecutable { function execute() external; } -contract NodeDriverAuth is IErrors, Initializable, Ownable { +contract NodeDriverAuth is Initializable, Ownable { SFCI internal sfc; NodeDriver internal driver; + error NotSFC(); + error NotDriver(); + error NotContract(); + error SelfCodeHashMismatch(); + error DriverCodeHashMismatch(); + error RecipientNotSFC(); + // Initialize NodeDriverAuth, NodeDriver and SFC in one call to allow fewer genesis transactions function initialize(address payable _sfc, address _driver, address _owner) external initializer { Ownable.initialize(_owner); diff --git a/contracts/sfc/SFC.sol b/contracts/sfc/SFC.sol index 293b824..5a3aa63 100644 --- a/contracts/sfc/SFC.sol +++ b/contracts/sfc/SFC.sol @@ -82,6 +82,10 @@ contract SFC is SFCBase, Version { return getEpochSnapshot[epoch].offlineBlocks[validatorID]; } + function getEpochEndBlock(uint256 epoch) public view returns (uint256) { + return getEpochSnapshot[epoch].endBlock; + } + function rewardsStash(address delegator, uint256 validatorID) public view returns (uint256) { Rewards memory stash = _rewardsStash[delegator][validatorID]; return stash.lockupBaseReward + stash.lockupExtraReward + stash.unlockedReward; @@ -389,6 +393,7 @@ contract SFC is SFCBase, Version { currentSealedEpoch = currentEpoch(); snapshot.endTime = _now(); + snapshot.endBlock = block.number; snapshot.baseRewardPerSecond = c.baseRewardPerSecond(); snapshot.totalSupply = totalSupply; } diff --git a/contracts/sfc/SFCBase.sol b/contracts/sfc/SFCBase.sol index cd6215d..164404f 100644 --- a/contracts/sfc/SFCBase.sol +++ b/contracts/sfc/SFCBase.sol @@ -3,15 +3,85 @@ pragma solidity ^0.8.9; import {Decimal} from "../common/Decimal.sol"; import {SFCState} from "./SFCState.sol"; -import {IErrors} from "../IErrors.sol"; -contract SFCBase is IErrors, SFCState { +contract SFCBase is SFCState { uint256 internal constant OK_STATUS = 0; uint256 internal constant WITHDRAWN_BIT = 1; uint256 internal constant OFFLINE_BIT = 1 << 3; uint256 internal constant DOUBLESIGN_BIT = 1 << 7; uint256 internal constant CHEATER_MASK = DOUBLESIGN_BIT; + // auth + error NotDriverAuth(); + error NotAuthorized(); + + // addresses + error ZeroAddress(); + error SameAddress(); + + // values + error ZeroAmount(); + error ZeroRewards(); + + // pubkeys + error PubkeyExists(); + error MalformedPubkey(); + error SamePubkey(); + error EmptyPubkey(); + error PubkeyAllowedOnlyOnce(); + + // redirections + error SameRedirectionAuthorizer(); + error Redirected(); + + // validators + error ValidatorNotExists(); + error ValidatorExists(); + error ValidatorNotActive(); + error ValidatorDelegationLimitExceeded(); + error WrongValidatorStatus(); + + // requests + error RequestedCompleted(); + error RequestExists(); + error RequestNotExists(); + + // transfers + error TransfersNotAllowed(); + error TransferFailed(); + + // updater + error SFCAlreadyUpdated(); + error SFCWrongVersion(); + error SFCGovAlreadyUpdated(); + error SFCWrongGovVersion(); + + // governance + error GovVotesRecountFailed(); + + // staking + error LockedStakeGreaterThanTotalStake(); + error InsufficientSelfStake(); + error NotEnoughUnlockedStake(); + error NotEnoughLockedStake(); + error NotEnoughTimePassed(); + error NotEnoughEpochsPassed(); + error StakeIsFullySlashed(); + error IncorrectDuration(); + error ValidatorLockupTooShort(); + error TooManyReLocks(); + error TooFrequentReLocks(); + error LockupDurationDecreased(); + error AlreadyLockedUp(); + error NotLockedUp(); + + // stashing + error NothingToStash(); + + // slashing + error ValidatorNotSlashed(); + error RefundRatioTooHigh(); + event DeactivatedValidator(uint256 indexed validatorID, uint256 deactivatedEpoch, uint256 deactivatedTime); event ChangedValidatorStatus(uint256 indexed validatorID, uint256 status); diff --git a/contracts/sfc/SFCI.sol b/contracts/sfc/SFCI.sol index 40d90f2..205fff2 100644 --- a/contracts/sfc/SFCI.sol +++ b/contracts/sfc/SFCI.sol @@ -44,6 +44,7 @@ interface SFCI { view returns ( uint256 endTime, + uint256 endBlock, uint256 epochFee, uint256 totalBaseRewardWeight, uint256 totalTxRewardWeight, @@ -137,6 +138,8 @@ interface SFCI { function getEpochOfflineBlocks(uint256 epoch, uint256 validatorID) external view returns (uint256); + function getEpochEndBlock(uint256 epoch) external view returns (uint256); + function rewardsStash(address delegator, uint256 validatorID) external view returns (uint256); function getLockedStake(address delegator, uint256 toValidatorID) external view returns (uint256); diff --git a/contracts/sfc/SFCState.sol b/contracts/sfc/SFCState.sol index 555a194..e19fadd 100644 --- a/contracts/sfc/SFCState.sol +++ b/contracts/sfc/SFCState.sol @@ -72,6 +72,7 @@ contract SFCState is Initializable, Ownable { mapping(uint256 => uint256) offlineBlocks; uint256[] validatorIDs; uint256 endTime; + uint256 endBlock; uint256 epochFee; uint256 totalBaseRewardWeight; uint256 totalTxRewardWeight; diff --git a/contracts/sfc/Updater.sol b/contracts/sfc/Updater.sol index d4516cb..634167a 100644 --- a/contracts/sfc/Updater.sol +++ b/contracts/sfc/Updater.sol @@ -8,7 +8,6 @@ import {ConstantsManager} from "./ConstantsManager.sol"; import {SFC} from "./SFC.sol"; import {SFCI} from "./SFCI.sol"; import {Version} from "../version/Version.sol"; -import {IErrors} from "../IErrors.sol"; interface GovI { function upgrade(address v) external; @@ -22,7 +21,7 @@ interface GovVersion { function version() external pure returns (bytes4); } -contract Updater is IErrors { +contract Updater { address public sfcFrom; address public sfcLib; address public sfcConsts; @@ -31,6 +30,12 @@ contract Updater is IErrors { address public voteBook; address public owner; + error ZeroAddress(); + error SFCAlreadyUpdated(); + error SFCWrongVersion(); + error SFCGovAlreadyUpdated(); + error SFCWrongGovVersion(); + constructor( address _sfcFrom, address _sfcLib, diff --git a/contracts/test/StubEvmWriter.sol b/contracts/test/StubEvmWriter.sol index 46882e4..1c50617 100644 --- a/contracts/test/StubEvmWriter.sol +++ b/contracts/test/StubEvmWriter.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.9; -import {EVMWriter} from "../sfc/NodeDriver.sol"; +import {IEvmWriter} from "../interfaces/IEVMWriter.sol"; -contract StubEvmWriter is EVMWriter { +contract StubEvmWriter is IEvmWriter { function setBalance(address acc, uint256 value) external {} function copyCode(address acc, address from) external {} diff --git a/contracts/test/UnitTestSFC.sol b/contracts/test/UnitTestSFC.sol index 9d81fa6..ef7d24d 100644 --- a/contracts/test/UnitTestSFC.sol +++ b/contracts/test/UnitTestSFC.sol @@ -5,7 +5,6 @@ import {Decimal} from "../common/Decimal.sol"; import {SFC} from "../sfc/SFC.sol"; import {SFCBase} from "../sfc/SFCBase.sol"; import {SFCLib} from "../sfc/SFCLib.sol"; -import {IErrors} from "../IErrors.sol"; import {NodeDriverAuth} from "../sfc/NodeDriverAuth.sol"; import {NodeDriver} from "../sfc/NodeDriver.sol"; import {UnitTestConstantsManager} from "./UnitTestConstantsManager.sol"; @@ -114,7 +113,7 @@ contract UnitTestNetworkInitializer { } } -interface SFCUnitTestI is IErrors { +interface SFCUnitTestI { function currentSealedEpoch() external view returns (uint256); function getEpochSnapshot( @@ -124,6 +123,7 @@ interface SFCUnitTestI is IErrors { view returns ( uint256 endTime, + uint256 endBlock, uint256 epochFee, uint256 totalBaseRewardWeight, uint256 totalTxRewardWeight, @@ -219,6 +219,8 @@ interface SFCUnitTestI is IErrors { function getEpochOfflineBlocks(uint256 epoch, uint256 validatorID) external view returns (uint256); + function getEpochEndBlock(uint256 epoch) external view returns (uint256); + function rewardsStash(address delegator, uint256 validatorID) external view returns (uint256); function getLockedStake(address delegator, uint256 toValidatorID) external view returns (uint256); diff --git a/test/NodeDriver.ts b/test/NodeDriver.ts index 1bd6475..d71f431 100644 --- a/test/NodeDriver.ts +++ b/test/NodeDriver.ts @@ -40,7 +40,7 @@ describe('NodeDriver', () => { const account = ethers.Wallet.createRandom(); await expect(this.nodeDriverAuth.connect(this.nonOwner).migrateTo(account)).to.be.revertedWithCustomError( this.nodeDriverAuth, - 'NotOwner', + 'OwnableUnauthorizedAccount', ); }); }); @@ -55,7 +55,7 @@ describe('NodeDriver', () => { const address = ethers.Wallet.createRandom(); await expect( this.nodeDriverAuth.connect(this.nonOwner).copyCode(this.sfc, address), - ).to.be.revertedWithCustomError(this.nodeDriverAuth, 'NotOwner'); + ).to.be.revertedWithCustomError(this.nodeDriverAuth, 'OwnableUnauthorizedAccount'); }); }); @@ -69,7 +69,7 @@ describe('NodeDriver', () => { it('Should revert when not owner', async function () { await expect(this.nodeDriverAuth.connect(this.nonOwner).updateNetworkVersion(1)).to.be.revertedWithCustomError( this.nodeDriverAuth, - 'NotOwner', + 'OwnableUnauthorizedAccount', ); }); }); @@ -82,7 +82,7 @@ describe('NodeDriver', () => { it('Should revert when not owner', async function () { await expect(this.nodeDriverAuth.connect(this.nonOwner).advanceEpochs(10)).to.be.revertedWithCustomError( this.nodeDriverAuth, - 'NotOwner', + 'OwnableUnauthorizedAccount', ); }); }); diff --git a/test/SFC.ts b/test/SFC.ts index 093e0c7..557ad9f 100644 --- a/test/SFC.ts +++ b/test/SFC.ts @@ -52,7 +52,7 @@ describe('SFC', () => { to: this.sfc, value: 1, }), - ).to.revertedWithCustomError(this.sfc, 'TransfersNotAllowed'); + ).to.revertedWithCustomError(this.sfcLib, 'TransfersNotAllowed'); }); describe('Genesis validator', () => { @@ -77,11 +77,14 @@ describe('SFC', () => { }); it('Should revert when sealEpoch not called by node', async function () { - await expect(this.sfc.sealEpoch([1], [1], [1], [1], 0)).to.be.revertedWithCustomError(this.sfc, 'NotDriverAuth'); + await expect(this.sfc.sealEpoch([1], [1], [1], [1], 0)).to.be.revertedWithCustomError( + this.sfcLib, + 'NotDriverAuth', + ); }); it('Should revert when SealEpochValidators not called by node', async function () { - await expect(this.sfc.sealEpochValidators([1])).to.be.revertedWithCustomError(this.sfc, 'NotDriverAuth'); + await expect(this.sfc.sealEpochValidators([1])).to.be.revertedWithCustomError(this.sfcLib, 'NotDriverAuth'); }); }); @@ -176,13 +179,13 @@ describe('SFC', () => { this.sfc .connect(this.validator) .createValidator(ethers.Wallet.createRandom().publicKey, { value: ethers.parseEther('0.1') }), - ).to.be.revertedWithCustomError(this.sfc, 'InsufficientSelfStake'); + ).to.be.revertedWithCustomError(this.sfcLib, 'InsufficientSelfStake'); }); it('Should revert when public key is empty while creating a validator', async function () { await expect( this.sfc.connect(this.validator).createValidator('0x', { value: ethers.parseEther('0.4') }), - ).to.be.revertedWithCustomError(this.sfc, 'EmptyPubkey'); + ).to.be.revertedWithCustomError(this.sfcLib, 'EmptyPubkey'); }); it('Should succeed and create two validators and return id of last validator', async function () { @@ -213,7 +216,7 @@ describe('SFC', () => { it('Should revert when staking to non-existing validator', async function () { await expect( this.sfc.connect(this.secondValidator).delegate(1, { value: ethers.parseEther('0.1') }), - ).to.be.revertedWithCustomError(this.sfc, 'ValidatorNotExists'); + ).to.be.revertedWithCustomError(this.sfcLib, 'ValidatorNotExists'); }); it('Should succeed and stake with different delegators', async function () { @@ -290,15 +293,14 @@ describe('SFC', () => { it('Should revert when transferring ownership if not owner', async function () { await expect(this.sfc.connect(this.user).transferOwnership(ethers.ZeroAddress)).to.be.revertedWithCustomError( this.nodeDriverAuth, - 'NotOwner', + 'OwnableUnauthorizedAccount', ); }); it('Should revert when transferring ownership to zero address', async function () { - await expect(this.sfc.transferOwnership(ethers.ZeroAddress)).to.be.revertedWithCustomError( - this.nodeDriverAuth, - 'ZeroAddress', - ); + await expect(this.sfc.transferOwnership(ethers.ZeroAddress)) + .to.be.revertedWithCustomError(this.nodeDriverAuth, 'OwnableInvalidOwner') + .withArgs(ethers.ZeroAddress); }); }); @@ -329,13 +331,13 @@ describe('SFC', () => { 0, 0, ), - ).to.be.revertedWithCustomError(this.sfc, 'NotDriverAuth'); + ).to.be.revertedWithCustomError(this.sfcLib, 'NotDriverAuth'); }); it('Should revert when setGenesisDelegation is not called not node', async function () { const delegator = ethers.Wallet.createRandom(); await expect(this.sfc.setGenesisDelegation(delegator, 1, 100, 0, 0, 0, 0, 0, 1000)).to.be.revertedWithCustomError( - this.sfc, + this.sfcLib, 'NotDriverAuth', ); }); @@ -414,6 +416,16 @@ describe('SFC', () => { expect(await this.sfc.currentEpoch.call()).to.equal(6); expect(await this.sfc.currentSealedEpoch()).to.equal(5); }); + + it('Should succeed and return endBlock', async function () { + const epochNumber = await this.sfc.currentEpoch(); + await this.sfc.enableNonNodeCalls(); + await this.sfc.sealEpoch([100, 101, 102], [100, 101, 102], [100, 101, 102], [100, 101, 102], 0); + const lastBlock = await ethers.provider.getBlockNumber(); + // endBlock is on second position + expect((await this.sfc.getEpochSnapshot(epochNumber))[1]).to.equal(lastBlock); + expect(await this.sfc.getEpochEndBlock(epochNumber)).to.equal(lastBlock); + }); }); }); @@ -431,7 +443,7 @@ describe('SFC', () => { this.sfc .connect(validator) .createValidator(ethers.Wallet.createRandom().publicKey, { value: ethers.parseEther('0.1') }), - ).to.be.revertedWithCustomError(this.sfc, 'InsufficientSelfStake'); + ).to.be.revertedWithCustomError(this.sfcLib, 'InsufficientSelfStake'); await node.handleTx( await this.sfc.connect(validator).createValidator(pubkey, { value: ethers.parseEther('0.3175') }), @@ -441,7 +453,7 @@ describe('SFC', () => { this.sfc .connect(validator) .createValidator(ethers.Wallet.createRandom().publicKey, { value: ethers.parseEther('0.5') }), - ).to.be.revertedWithCustomError(this.sfc, 'ValidatorExists'); + ).to.be.revertedWithCustomError(this.sfcLib, 'ValidatorExists'); await node.handleTx( await this.sfc.connect(secondValidator).createValidator(secondPubkey, { value: ethers.parseEther('0.5') }), @@ -759,7 +771,7 @@ describe('SFC', () => { it('Should revert when deactivating validator if not Node', async function () { await this.sfc.disableNonNodeCalls(); await expect(this.sfc.deactivateValidator(this.validatorId, 0)).to.be.revertedWithCustomError( - this.sfc, + this.sfcLib, 'NotDriverAuth', ); }); @@ -999,7 +1011,7 @@ describe('SFC', () => { it('Should revert when calling deactivateValidator with wrong status', async function () { await expect(this.sfc.deactivateValidator(1, 0)).to.be.revertedWithCustomError( - this.sfc, + this.sfcLib, 'WrongValidatorStatus', ); }); @@ -1087,11 +1099,11 @@ describe('SFC', () => { describe('Epoch getters', () => { it('Should revert when trying to unlock stake if not lockedup', async function () { - await expect(this.sfc.unlockStake(1, 10)).to.be.revertedWithCustomError(this.sfc, 'NotLockedUp'); + await expect(this.sfc.unlockStake(1, 10)).to.be.revertedWithCustomError(this.sfcLib, 'NotLockedUp'); }); it('Should revert when trying to unlock stake if amount is 0', async function () { - await expect(this.sfc.unlockStake(1, 0)).to.be.revertedWithCustomError(this.sfc, 'ZeroAmount'); + await expect(this.sfc.unlockStake(1, 0)).to.be.revertedWithCustomError(this.sfcLib, 'ZeroAmount'); }); it('Should succeed and return slashed status', async function () { @@ -1099,12 +1111,12 @@ describe('SFC', () => { }); it('Should revert when delegating to an unexisting validator', async function () { - await expect(this.sfc.delegate(4)).to.be.revertedWithCustomError(this.sfc, 'ValidatorNotExists'); + await expect(this.sfc.delegate(4)).to.be.revertedWithCustomError(this.sfcLib, 'ValidatorNotExists'); }); it('Should revert when delegating to an unexisting validator (2)', async function () { await expect(this.sfc.delegate(4, { value: ethers.parseEther('1') })).to.be.revertedWithCustomError( - this.sfc, + this.sfcLib, 'ValidatorNotExists', ); }); @@ -1205,20 +1217,23 @@ describe('SFC', () => { it('Should revert when withdrawing nonexistent request', async function () { await expect(this.sfc.withdraw(this.validatorId, 0)).to.be.revertedWithCustomError( - this.sfc, + this.sfcLib, 'RequestNotExists', ); }); it('Should revert when undelegating 0 amount', async function () { await this.blockchainNode.sealEpoch(1_000); - await expect(this.sfc.undelegate(this.validatorId, 0, 0)).to.be.revertedWithCustomError(this.sfc, 'ZeroAmount'); + await expect(this.sfc.undelegate(this.validatorId, 0, 0)).to.be.revertedWithCustomError( + this.sfcLib, + 'ZeroAmount', + ); }); it('Should revert when undelegating if not enough unlocked stake', async function () { await this.blockchainNode.sealEpoch(1_000); await expect(this.sfc.undelegate(this.validatorId, 0, 10)).to.be.revertedWithCustomError( - this.sfc, + this.sfcLib, 'NotEnoughUnlockedStake', ); }); @@ -1228,7 +1243,7 @@ describe('SFC', () => { await this.sfc.connect(this.thirdDelegator).delegate(this.validatorId, { value: ethers.parseEther('1') }); await expect( this.sfc.connect(this.thirdDelegator).unlockStake(this.validatorId, 10), - ).to.be.revertedWithCustomError(this.sfc, 'NotLockedUp'); + ).to.be.revertedWithCustomError(this.sfcLib, 'NotLockedUp'); }); it('Should succeed and return the unlocked stake', async function () { @@ -1244,7 +1259,7 @@ describe('SFC', () => { await this.blockchainNode.sealEpoch(1_000); await expect( this.sfc.connect(this.thirdDelegator).claimRewards(this.validatorId), - ).to.be.revertedWithCustomError(this.sfc, 'ZeroRewards'); + ).to.be.revertedWithCustomError(this.sfcLib, 'ZeroRewards'); }); }); @@ -1262,7 +1277,7 @@ describe('SFC', () => { this.sfc .connect(this.thirdDelegator) .lockStake(this.validatorId, 2 * 60 * 60 * 24 * 365, ethers.parseEther('0')), - ).to.be.revertedWithCustomError(this.sfc, 'ZeroAmount'); + ).to.be.revertedWithCustomError(this.sfcLib, 'ZeroAmount'); }); it('Should revert when locking for more than a year', async function () { @@ -1272,7 +1287,7 @@ describe('SFC', () => { this.sfc .connect(this.thirdDelegator) .lockStake(this.thirdValidatorId, 2 * 60 * 60 * 24 * 365, ethers.parseEther('1')), - ).to.be.revertedWithCustomError(this.sfc, 'IncorrectDuration'); + ).to.be.revertedWithCustomError(this.sfcLib, 'IncorrectDuration'); }); it('Should revert when locking for more than a validator lockup period', async function () { @@ -1282,7 +1297,7 @@ describe('SFC', () => { this.sfc .connect(this.thirdDelegator) .lockStake(this.thirdValidatorId, 60 * 60 * 24 * 364, ethers.parseEther('1')), - ).to.be.revertedWithCustomError(this.sfc, 'ValidatorLockupTooShort'); + ).to.be.revertedWithCustomError(this.sfcLib, 'ValidatorLockupTooShort'); await this.sfc .connect(this.thirdDelegator) .lockStake(this.thirdValidatorId, 60 * 60 * 24 * 363, ethers.parseEther('1')); @@ -1306,7 +1321,7 @@ describe('SFC', () => { await this.blockchainNode.sealEpoch(60 * 60 * 24 * 14); await expect( this.sfc.unlockStake(this.thirdValidatorId, ethers.parseEther('10')), - ).to.be.revertedWithCustomError(this.sfc, 'NotLockedUp'); + ).to.be.revertedWithCustomError(this.sfcLib, 'NotLockedUp'); }); it('Should revert when unlocking more than locked stake', async function () { @@ -1318,7 +1333,7 @@ describe('SFC', () => { await this.blockchainNode.sealEpoch(60 * 60 * 24 * 14); await expect( this.sfc.connect(this.thirdDelegator).unlockStake(this.thirdValidatorId, ethers.parseEther('10')), - ).to.be.revertedWithCustomError(this.sfc, 'NotEnoughLockedStake'); + ).to.be.revertedWithCustomError(this.sfcLib, 'NotEnoughLockedStake'); }); it('Should succeed and scale unlocking penalty', async function () { @@ -1349,7 +1364,7 @@ describe('SFC', () => { await expect( this.sfc.connect(this.thirdDelegator).unlockStake(this.thirdValidatorId, ethers.parseEther('0.51')), - ).to.be.revertedWithCustomError(this.sfc, 'NotEnoughLockedStake'); + ).to.be.revertedWithCustomError(this.sfcLib, 'NotEnoughLockedStake'); expect( await this.sfc .connect(this.thirdDelegator) @@ -1390,7 +1405,7 @@ describe('SFC', () => { await expect( this.sfc.connect(this.thirdDelegator).unlockStake(this.thirdValidatorId, ethers.parseEther('0.51')), - ).to.be.revertedWithCustomError(this.sfc, 'NotEnoughLockedStake'); + ).to.be.revertedWithCustomError(this.sfcLib, 'NotEnoughLockedStake'); expect( await this.sfc .connect(this.thirdDelegator) @@ -1408,7 +1423,7 @@ describe('SFC', () => { await expect( this.sfc.connect(this.thirdDelegator).unlockStake(this.thirdValidatorId, ethers.parseEther('1.51')), - ).to.be.revertedWithCustomError(this.sfc, 'NotEnoughLockedStake'); + ).to.be.revertedWithCustomError(this.sfcLib, 'NotEnoughLockedStake'); expect( await this.sfc .connect(this.thirdDelegator) @@ -1470,13 +1485,16 @@ describe('SFC', () => { await expect( this.sfc.connect(this.validator).updateSlashingRefundRatio(this.thirdValidatorId, 1), - ).to.be.revertedWithCustomError(this.sfc, 'ValidatorNotSlashed'); + ).to.be.revertedWithCustomError(this.sfcLib, 'ValidatorNotSlashed'); await this.blockchainNode.sealEpoch(60 * 60 * 24 * 14); }); it('Should revert when syncing if validator does not exist', async function () { - await expect(this.sfc._syncValidator(33, false)).to.be.revertedWithCustomError(this.sfc, 'ValidatorNotExists'); + await expect(this.sfc._syncValidator(33, false)).to.be.revertedWithCustomError( + this.sfcLib, + 'ValidatorNotExists', + ); }); }); }); @@ -1679,7 +1697,7 @@ describe('SFC', () => { await this.blockchainNode.sealEpoch(60 * 60 * 24); await expect( this.sfc.connect(this.firstDelegator).relockStake(this.validatorId, 60 * 60 * 24 * 20, ethers.parseEther('0')), - ).to.be.revertedWithCustomError(this.sfc, 'TooFrequentReLocks'); + ).to.be.revertedWithCustomError(this.sfcLib, 'TooFrequentReLocks'); // 4 await this.sfc.advanceTime(60 * 60 * 24 * 14); @@ -1689,7 +1707,7 @@ describe('SFC', () => { await this.blockchainNode.sealEpoch(60 * 60 * 24); await expect( this.sfc.connect(this.firstDelegator).relockStake(this.validatorId, 60 * 60 * 24 * 20, ethers.parseEther('0')), - ).to.be.revertedWithCustomError(this.sfc, 'TooFrequentReLocks'); + ).to.be.revertedWithCustomError(this.sfcLib, 'TooFrequentReLocks'); for (let i = 5; i <= 40; i++) { // 5-40