diff --git a/audits/internal4/README.md b/audits/internal4/README.md index d916194f..dd3709ea 100644 --- a/audits/internal4/README.md +++ b/audits/internal4/README.md @@ -210,5 +210,58 @@ The review has been performed based on the contract code in the following reposi `https://github.com/valory-xyz/autonolas-registries`
commit: `v1.1.7.pre-internal-audit` or `387ba93deeb36849c1c205711b97a2c6da0f2745`
Re-audit after extract Mech contracts into a separate repo.
+[slither-full-25-10-23](https://github.com/valory-xyz/autonolas-registries/blob/main/audits/internal4/analysis/slither_full_25_10_23.txt) I don't see any new problems after optimization.
+ +### Security issues. Updated 29-11-23 +The review has been performed based on the contract code in the following repository:
+`https://github.com/valory-xyz/autonolas-registries`
+commit: `tag: v1.1.8-pre-internal-audit` or `99eef0ffd2450311b2770faceac37191a13af5cb`
+Re-audit after changes ServiceStakingBase.
+[slither-full](https://github.com/valory-xyz/autonolas-registries/blob/main/audits/internal4/analysis/slither_full.txt) + +#### Notes (critical improvements) +Due to the impossibility of manually assessing the correctness of the logic at the boundaries of ```_evict``` +"evict" must be part of ServiceStaking.t.sol (fuzzing)
+or equivalent in js tests
+Clarification required on the source code of the tests:
+```solidity +We need cases: +numEvictServices == 1 +numEvictServices > 1 +numEvictServices == setServiceIds +numEvictServices < setServiceIds + ++ is it possible to exclude a special case + // Deal with the very first element + // Get the evicted service index + idx = serviceIndexes[0]; + // Assign last service Id to the index that points to the evicted service Id + setServiceIds[idx] = setServiceIds[totalNumServices - 1]; + // Pop the last element + setServiceIds.pop(); + and move it under for() +``` + +#### Notes (unstake scenario) +```solidity + // Check that the service has staked long enough, or if there are no rewards left + uint256 ts = block.timestamp - tsStart; + if (ts <= maxAllowedInactivity && availableRewards > 0) { + revert NotEnoughTimeStaked(serviceId, ts, maxAllowedInactivity); + } + + 1. Service staking + 2. Service inactive all time // if (serviceInactivity[i] > maxAllowedInactivity) + 2a. Service evict // _evict(evictServiceIds, serviceInactivity, numServices); + 3. Service try unstake in 1s after 2a. // => tsStart = sInfo.tsStart; ts = block.timestamp - tsStart; + ??? What will happen? + + What is the expected scenario for this barrier? + // Check that the service has staked long enough, or if there are no rewards left + uint256 ts = block.timestamp - tsStart; + if (ts <= maxAllowedInactivity && availableRewards > 0) { + revert NotEnoughTimeStaked(serviceId, ts, maxAllowedInactivity); + } +``` diff --git a/audits/internal4/analysis/contracts/ServiceStakingBase-flatten.sol b/audits/internal4/analysis/contracts/ServiceStakingBase-flatten.sol index a9580b50..50095c1a 100644 --- a/audits/internal4/analysis/contracts/ServiceStakingBase-flatten.sol +++ b/audits/internal4/analysis/contracts/ServiceStakingBase-flatten.sol @@ -4,7 +4,6 @@ // File contracts/interfaces/IErrorsRegistries.sol -// Original license: SPDX_License_Identifier: MIT pragma solidity ^0.8.21; /// @dev Errors. @@ -422,9 +421,19 @@ error LowerThan(uint256 provided, uint256 expected); /// @param serviceId Service Id. error WrongServiceConfiguration(uint256 serviceId); -/// @dev Service is not staked. +/// @dev Service is not unstaked. /// @param serviceId Service Id. -error ServiceNotStaked(uint256 serviceId); +error ServiceNotUnstaked(uint256 serviceId); + +/// @dev Service is not found. +/// @param serviceId Service Id. +error ServiceNotFound(uint256 serviceId); + +/// @dev Service was not staked a minimum required time. +/// @param serviceId Service Id. +/// @param tsProvided Time the service is staked for. +/// @param tsExpected Minimum time the service needs to be staked for. +error NotEnoughTimeStaked(uint256 serviceId, uint256 tsProvided, uint256 tsExpected); // Service Info struct struct ServiceInfo { @@ -438,6 +447,8 @@ struct ServiceInfo { uint256 tsStart; // Accumulated service staking reward uint256 reward; + // Accumulated inactivity that might lead to the service eviction + uint256 inactivity; } /// @title ServiceStakingBase - Base abstract smart contract for staking a service by its owner @@ -445,6 +456,12 @@ struct ServiceInfo { /// @author Andrey Lebedev - /// @author Mariapia Moscatiello - abstract contract ServiceStakingBase is ERC721TokenReceiver, IErrorsRegistries { + enum ServiceStakingState { + Unstaked, + Staked, + Evicted + } + // Input staking parameters struct StakingParams { // Maximum number of staking services @@ -453,6 +470,8 @@ abstract contract ServiceStakingBase is ERC721TokenReceiver, IErrorsRegistries { uint256 rewardsPerSecond; // Minimum service staking deposit value required for staking uint256 minStakingDeposit; + // Max number of accumulated inactivity periods after which the service is evicted + uint256 maxNumInactivityPeriods; // Liveness period uint256 livenessPeriod; // Liveness ratio in the format of 1e18 @@ -467,10 +486,13 @@ abstract contract ServiceStakingBase is ERC721TokenReceiver, IErrorsRegistries { bytes32 configHash; } - event ServiceStaked(uint256 indexed serviceId, address indexed owner, address indexed multisig, uint256[] nonces); - event Checkpoint(uint256 availableRewards, uint256 numServices); - event ServiceUnstaked(uint256 indexed serviceId, address indexed owner, address indexed multisig, uint256[] nonces, - uint256 reward, uint256 tsStart); + event ServiceStaked(uint256 epoch, uint256 indexed serviceId, address indexed owner, address indexed multisig, + uint256[] nonces); + event Checkpoint(uint256 indexed epoch, uint256 availableRewards, uint256[] serviceIds, uint256[] rewards); + event ServiceUnstaked(uint256 epoch, uint256 indexed serviceId, address indexed owner, address indexed multisig, + uint256[] nonces, uint256 reward); + event ServicesEvicted(uint256 indexed epoch, uint256[] serviceIds, address[] owners, address[] multisigs, + uint256[] serviceInactivity); event Deposit(address indexed sender, uint256 amount, uint256 balance, uint256 availableRewards); event Withdraw(address indexed to, uint256 amount); @@ -483,6 +505,8 @@ abstract contract ServiceStakingBase is ERC721TokenReceiver, IErrorsRegistries { // Minimum service staking deposit value required for staking // The staking deposit must be always greater than 1 in order to distinguish between native and ERC20 tokens uint256 public immutable minStakingDeposit; + // Max number of accumulated inactivity periods after which the service is evicted + uint256 public immutable maxNumInactivityPeriods; // Liveness period uint256 public immutable livenessPeriod; // Liveness ratio in the format of 1e18 @@ -497,7 +521,11 @@ abstract contract ServiceStakingBase is ERC721TokenReceiver, IErrorsRegistries { address public immutable serviceRegistry; // Approved multisig proxy hash bytes32 public immutable proxyHash; + // Max allowed inactivity + uint256 public immutable maxAllowedInactivity; + // Epoch counter + uint256 public epochCounter; // Token / ETH balance uint256 public balance; // Token / ETH available rewards @@ -519,7 +547,7 @@ abstract contract ServiceStakingBase is ERC721TokenReceiver, IErrorsRegistries { // Initial checks if (_stakingParams.maxNumServices == 0 || _stakingParams.rewardsPerSecond == 0 || _stakingParams.livenessPeriod == 0 || _stakingParams.livenessRatio == 0 || - _stakingParams.numAgentInstances == 0) { + _stakingParams.numAgentInstances == 0 || _stakingParams.maxNumInactivityPeriods == 0) { revert ZeroValue(); } if (_stakingParams.minStakingDeposit < 2) { @@ -533,6 +561,7 @@ abstract contract ServiceStakingBase is ERC721TokenReceiver, IErrorsRegistries { maxNumServices = _stakingParams.maxNumServices; rewardsPerSecond = _stakingParams.rewardsPerSecond; minStakingDeposit = _stakingParams.minStakingDeposit; + maxNumInactivityPeriods = _stakingParams.maxNumInactivityPeriods; livenessPeriod = _stakingParams.livenessPeriod; livenessRatio = _stakingParams.livenessRatio; numAgentInstances = _stakingParams.numAgentInstances; @@ -561,6 +590,9 @@ abstract contract ServiceStakingBase is ERC721TokenReceiver, IErrorsRegistries { // Record provided multisig proxy bytecode hash proxyHash = _proxyHash; + // Calculate max allowed inactivity + maxAllowedInactivity = _stakingParams.maxNumInactivityPeriods * livenessPeriod; + // Set the checkpoint timestamp to be the deployment one tsCheckpoint = block.timestamp; } @@ -580,6 +612,8 @@ abstract contract ServiceStakingBase is ERC721TokenReceiver, IErrorsRegistries { function _withdraw(address to, uint256 amount) internal virtual; /// @dev Stakes the service. + /// @notice Each service must be staked for a minimum of maxAllowedInactivity time, or until the funds are not zero. + /// maxAllowedInactivity = maxNumInactivityPeriods * livenessPeriod /// @param serviceId Service Id. function stake(uint256 serviceId) external { // Check if there available rewards @@ -587,6 +621,13 @@ abstract contract ServiceStakingBase is ERC721TokenReceiver, IErrorsRegistries { revert NoRewardsAvailable(); } + // Check if the evicted service has not yet unstaked + ServiceInfo storage sInfo = mapServiceInfo[serviceId]; + // tsStart being greater than zero means that the service was not yet unstaked: still staking or evicted + if (sInfo.tsStart > 0) { + revert ServiceNotUnstaked(serviceId); + } + // Check for the maximum number of staking services uint256 numStakingServices = setServiceIds.length; if (numStakingServices == maxNumServices) { @@ -639,7 +680,6 @@ abstract contract ServiceStakingBase is ERC721TokenReceiver, IErrorsRegistries { _checkTokenStakingDeposit(serviceId, service.securityDeposit); // ServiceInfo struct will be an empty one since otherwise the safeTransferFrom above would fail - ServiceInfo storage sInfo = mapServiceInfo[serviceId]; sInfo.multisig = service.multisig; sInfo.owner = msg.sender; uint256[] memory nonces = _getMultisigNonces(service.multisig); @@ -652,7 +692,7 @@ abstract contract ServiceStakingBase is ERC721TokenReceiver, IErrorsRegistries { // Transfer the service for staking IService(serviceRegistry).safeTransferFrom(msg.sender, address(this), serviceId); - emit ServiceStaked(serviceId, msg.sender, service.multisig, nonces); + emit ServiceStaked(epochCounter, serviceId, msg.sender, service.multisig, nonces); } /// @dev Gets service multisig nonces. @@ -694,6 +734,7 @@ abstract contract ServiceStakingBase is ERC721TokenReceiver, IErrorsRegistries { /// @param eligibleServiceRewards Corresponding rewards for eligible service Ids. /// @param serviceIds All the staking service Ids. /// @param serviceNonces Current service nonces. + /// @param serviceInactivity Service inactivity records. function _calculateStakingRewards() internal view returns ( uint256 lastAvailableRewards, uint256 numServices, @@ -701,7 +742,8 @@ abstract contract ServiceStakingBase is ERC721TokenReceiver, IErrorsRegistries { uint256[] memory eligibleServiceIds, uint256[] memory eligibleServiceRewards, uint256[] memory serviceIds, - uint256[][] memory serviceNonces + uint256[][] memory serviceNonces, + uint256[] memory serviceInactivity ) { // Check the last checkpoint timestamp and the liveness period, also check for available rewards to be not zero @@ -716,6 +758,7 @@ abstract contract ServiceStakingBase is ERC721TokenReceiver, IErrorsRegistries { eligibleServiceIds = new uint256[](size); eligibleServiceRewards = new uint256[](size); serviceNonces = new uint256[][](size); + serviceInactivity = new uint256[](size); // Calculate each staked service reward eligibility for (uint256 i = 0; i < size; ++i) { @@ -738,47 +781,116 @@ abstract contract ServiceStakingBase is ERC721TokenReceiver, IErrorsRegistries { } // Calculate the liveness ratio in 1e18 value - // This subtraction is always positive or zero, as the last checkpoint can be at most block.timestamp + // This subtraction is always positive or zero, as the last checkpoint is at most block.timestamp ts = block.timestamp - serviceCheckpoint; bool ratioPass = _isRatioPass(serviceNonces[i], sInfo.nonces, ts); // Record the reward for the service if it has provided enough transactions if (ratioPass) { // Calculate the reward up until now and record its value for the corresponding service - uint256 reward = rewardsPerSecond * ts; - totalRewards += reward; - eligibleServiceRewards[numServices] = reward; + eligibleServiceRewards[numServices] = rewardsPerSecond * ts; + totalRewards += eligibleServiceRewards[numServices]; eligibleServiceIds[numServices] = serviceIds[i]; ++numServices; + } else { + serviceInactivity[i] = ts; } } } } + /// @dev Evicts services due to their extended inactivity. + /// @param evictServiceIds Service Ids to be evicted. + /// @param serviceInactivity Corresponding service inactivity records. + /// @param numEvictServices Number of services to evict. + function _evict( + uint256[] memory evictServiceIds, + uint256[] memory serviceInactivity, + uint256 numEvictServices + ) internal + { + // Get the total number of staked services + // All the passed arrays have the length of the number of staked services + uint256 totalNumServices = evictServiceIds.length; + + // Get arrays of exact sizes + uint256[] memory serviceIds = new uint256[](numEvictServices); + address[] memory owners = new address[](numEvictServices); + address[] memory multisigs = new address[](numEvictServices); + uint256[] memory inactivity = new uint256[](numEvictServices); + uint256[] memory serviceIndexes = new uint256[](numEvictServices); + + // Fill in arrays + uint256 sCounter; + uint256 serviceId; + for (uint256 i = 0; i < totalNumServices; ++i) { + if (evictServiceIds[i] > 0) { + serviceId = evictServiceIds[i]; + serviceIds[sCounter] = serviceId; + + ServiceInfo storage sInfo = mapServiceInfo[serviceId]; + owners[sCounter] = sInfo.owner; + multisigs[sCounter] = sInfo.multisig; + inactivity[sCounter] = serviceInactivity[i]; + serviceIndexes[sCounter] = i; + sCounter++; + } + } + + // Evict services from the global set of staked services + uint256 idx; + for (uint256 i = numEvictServices - 1; i > 0; --i) { + // Decrease the number of services + totalNumServices--; + // Get the evicted service index + idx = serviceIndexes[i]; + // Assign last service Id to the index that points to the evicted service Id + setServiceIds[idx] = setServiceIds[totalNumServices]; + // Pop the last element + setServiceIds.pop(); + } + + // Deal with the very first element + // Get the evicted service index + idx = serviceIndexes[0]; + // Assign last service Id to the index that points to the evicted service Id + setServiceIds[idx] = setServiceIds[totalNumServices - 1]; + // Pop the last element + setServiceIds.pop(); + + emit ServicesEvicted(epochCounter, serviceIds, owners, multisigs, inactivity); + } + /// @dev Checkpoint to allocate rewards up until a current time. - /// @return All staking service Ids. - /// @return All staking updated nonces. - /// @return Number of reward-eligible staking services during current checkpoint period. - /// @return Eligible service Ids. - /// @return Eligible service rewards. - /// @return success True, if the checkpoint was successful. + /// @return All staking service Ids (including evicted ones within a current epoch). + /// @return All staking updated nonces (including evicted ones within a current epoch). + /// @return Set of reward-eligible service Ids. + /// @return Corresponding set of reward-eligible service rewards. + /// @return evictServiceIds Evicted service Ids. function checkpoint() public returns ( uint256[] memory, uint256[][] memory, - uint256, uint256[] memory, uint256[] memory, - bool success + uint256[] memory evictServiceIds ) { // Calculate staking rewards (uint256 lastAvailableRewards, uint256 numServices, uint256 totalRewards, uint256[] memory eligibleServiceIds, uint256[] memory eligibleServiceRewards, - uint256[] memory serviceIds, uint256[][] memory serviceNonces) = _calculateStakingRewards(); + uint256[] memory serviceIds, uint256[][] memory serviceNonces, + uint256[] memory serviceInactivity) = _calculateStakingRewards(); + + // Get arrays for eligible service Ids and rewards of exact size + uint256[] memory finalEligibleServiceIds; + uint256[] memory finalEligibleServiceRewards; + evictServiceIds = new uint256[](serviceIds.length); + uint256 curServiceId; // If there are eligible services, proceed with staking calculation and update rewards if (numServices > 0) { - uint256 curServiceId; + finalEligibleServiceIds = new uint256[](numServices); + finalEligibleServiceRewards = new uint256[](numServices); // If total allocated rewards are not enough, adjust the reward value if (totalRewards > lastAvailableRewards) { // Traverse all the eligible services and adjust their rewards proportional to leftovers @@ -791,6 +903,8 @@ abstract contract ServiceStakingBase is ERC721TokenReceiver, IErrorsRegistries { updatedTotalRewards += updatedReward; // Add reward to the overall service reward curServiceId = eligibleServiceIds[i]; + finalEligibleServiceIds[i] = eligibleServiceIds[i]; + finalEligibleServiceRewards[i] = updatedReward; mapServiceInfo[curServiceId].reward += updatedReward; } @@ -798,10 +912,12 @@ abstract contract ServiceStakingBase is ERC721TokenReceiver, IErrorsRegistries { updatedReward = (eligibleServiceRewards[0] * lastAvailableRewards) / totalRewards; updatedTotalRewards += updatedReward; curServiceId = eligibleServiceIds[0]; + finalEligibleServiceIds[0] = eligibleServiceIds[0]; // If the reward adjustment happened to have small leftovers, add it to the first service if (lastAvailableRewards > updatedTotalRewards) { updatedReward += lastAvailableRewards - updatedTotalRewards; } + finalEligibleServiceRewards[0] = updatedReward; // Add reward to the overall service reward mapServiceInfo[curServiceId].reward += updatedReward; // Set available rewards to zero @@ -811,6 +927,8 @@ abstract contract ServiceStakingBase is ERC721TokenReceiver, IErrorsRegistries { for (uint256 i = 0; i < numServices; ++i) { // Add reward to the service overall reward curServiceId = eligibleServiceIds[i]; + finalEligibleServiceIds[i] = eligibleServiceIds[i]; + finalEligibleServiceRewards[i] = eligibleServiceRewards[i]; mapServiceInfo[curServiceId].reward += eligibleServiceRewards[i]; } @@ -822,24 +940,50 @@ abstract contract ServiceStakingBase is ERC721TokenReceiver, IErrorsRegistries { availableRewards = lastAvailableRewards; } - // If service nonces were updated, then the checkpoint takes place, otherwise only service Ids are returned - if (serviceNonces.length > 0) { - // Updated current service nonces + // If service Ids are returned, then the checkpoint takes place + if (serviceIds.length > 0) { + numServices = 0; + // Record service inactivities and updated current service nonces for (uint256 i = 0; i < serviceIds.length; ++i) { // Get the current service Id - uint256 curServiceId = serviceIds[i]; + curServiceId = serviceIds[i]; + // Record service nonces mapServiceInfo[curServiceId].nonces = serviceNonces[i]; + + // Increase service inactivity if it is greater than zero + if (serviceInactivity[i] > 0) { + // Get the overall continuous service inactivity + serviceInactivity[i] = mapServiceInfo[curServiceId].inactivity + serviceInactivity[i]; + mapServiceInfo[curServiceId].inactivity = serviceInactivity[i]; + // Check for the maximum allowed inactivity time + if (serviceInactivity[i] > maxAllowedInactivity) { + // Evict a service if it has been inactive for more than a maximum allowed inactivity time + evictServiceIds[i] = curServiceId; + // Increase number of evicted services + numServices++; + } + } else { + // Otherwise, set it back to zero + mapServiceInfo[curServiceId].inactivity = 0; + } + } + + // Evict inactive services + if (numServices > 0) { + _evict(evictServiceIds, serviceInactivity, numServices); } // Record the current timestamp such that next calculations start from this point of time tsCheckpoint = block.timestamp; - success = true; + // Increase the epoch counter + uint256 eCounter = epochCounter; + epochCounter = eCounter + 1; - emit Checkpoint(lastAvailableRewards, numServices); + emit Checkpoint(eCounter, lastAvailableRewards, finalEligibleServiceIds, finalEligibleServiceRewards); } - return (serviceIds, serviceNonces, numServices, eligibleServiceIds, eligibleServiceRewards, success); + return (serviceIds, serviceNonces, finalEligibleServiceIds, finalEligibleServiceRewards, evictServiceIds); } /// @dev Unstakes the service. @@ -851,19 +995,36 @@ abstract contract ServiceStakingBase is ERC721TokenReceiver, IErrorsRegistries { revert OwnerOnly(msg.sender, sInfo.owner); } + // Get the staking start time + // Note that if the service info exists, the service is staked or evicted, and thus start time is always valid + uint256 tsStart = sInfo.tsStart; + + // Check that the service has staked long enough, or if there are no rewards left + uint256 ts = block.timestamp - tsStart; + if (ts <= maxAllowedInactivity && availableRewards > 0) { + revert NotEnoughTimeStaked(serviceId, ts, maxAllowedInactivity); + } + // Call the checkpoint - (uint256[] memory serviceIds, , , , , bool success) = checkpoint(); + (uint256[] memory serviceIds, , , , uint256[] memory evictServiceIds) = checkpoint(); // If the checkpoint was not successful, the serviceIds set is not returned and needs to be allocated - if (!success) { + if (serviceIds.length == 0) { serviceIds = getServiceIds(); + evictServiceIds = new uint256[](serviceIds.length); } // Get the service index in the set of services // The index must always exist as the service is currently staked, otherwise it has no record in the map uint256 idx; + bool inSet; for (; idx < serviceIds.length; ++idx) { - if (serviceIds[idx] == serviceId) { + // Service is still in a global staking set if it is found in the services set, + // and is not present in the evicted set + if (evictServiceIds[idx] == serviceId) { + break; + } else if (serviceIds[idx] == serviceId) { + inSet = true; break; } } @@ -871,7 +1032,6 @@ abstract contract ServiceStakingBase is ERC721TokenReceiver, IErrorsRegistries { // Get the unstaked service data uint256 reward = sInfo.reward; uint256[] memory nonces = sInfo.nonces; - uint256 tsStart = sInfo.tsStart; address multisig = sInfo.multisig; // Clear all the data about the unstaked service @@ -879,8 +1039,11 @@ abstract contract ServiceStakingBase is ERC721TokenReceiver, IErrorsRegistries { delete mapServiceInfo[serviceId]; // Update the set of staked service Ids - setServiceIds[idx] = setServiceIds[setServiceIds.length - 1]; - setServiceIds.pop(); + // If the index was not found, the service was evicted and is not part of staked services set + if (inSet) { + setServiceIds[idx] = setServiceIds[setServiceIds.length - 1]; + setServiceIds.pop(); + } // Transfer the service back to the owner IService(serviceRegistry).safeTransferFrom(address(this), msg.sender, serviceId); @@ -890,25 +1053,16 @@ abstract contract ServiceStakingBase is ERC721TokenReceiver, IErrorsRegistries { _withdraw(multisig, reward); } - emit ServiceUnstaked(serviceId, msg.sender, multisig, nonces, reward, tsStart); + emit ServiceUnstaked(epochCounter, serviceId, msg.sender, multisig, nonces, reward); } - /// @dev Calculates service staking reward at current timestamp. + /// @dev Calculates service staking reward during the last checkpoint period. /// @param serviceId Service Id. /// @return reward Service reward. - function calculateServiceStakingReward(uint256 serviceId) external view returns (uint256 reward) { - // Get current service reward - ServiceInfo memory sInfo = mapServiceInfo[serviceId]; - reward = sInfo.reward; - - // Check if the service is staked - if (sInfo.tsStart == 0) { - revert ServiceNotStaked(serviceId); - } - + function calculateServiceStakingLastReward(uint256 serviceId) public view returns (uint256 reward) { // Calculate overall staking rewards (uint256 lastAvailableRewards, uint256 numServices, uint256 totalRewards, uint256[] memory eligibleServiceIds, - uint256[] memory eligibleServiceRewards, , ) = _calculateStakingRewards(); + uint256[] memory eligibleServiceRewards, , , ) = _calculateStakingRewards(); // If there are eligible services, proceed with staking calculation and update rewards for the service Id for (uint256 i = 0; i < numServices; ++i) { @@ -916,33 +1070,37 @@ abstract contract ServiceStakingBase is ERC721TokenReceiver, IErrorsRegistries { if (eligibleServiceIds[i] == serviceId) { // If total allocated rewards are not enough, adjust the reward value if (totalRewards > lastAvailableRewards) { - reward += (eligibleServiceRewards[i] * lastAvailableRewards) / totalRewards; + reward = (eligibleServiceRewards[i] * lastAvailableRewards) / totalRewards; } else { - reward += eligibleServiceRewards[i]; + reward = eligibleServiceRewards[i]; } break; } } } - /// @dev Gets staked service Ids. - /// @return serviceIds Staked service Ids. - function getServiceIds() public view returns (uint256[] memory serviceIds) { - // Get the number of service Ids - uint256 size = setServiceIds.length; - serviceIds = new uint256[](size); + /// @dev Calculates overall service staking reward at current timestamp. + /// @param serviceId Service Id. + /// @return reward Service reward. + function calculateServiceStakingReward(uint256 serviceId) external view returns (uint256 reward) { + // Get current service reward + ServiceInfo memory sInfo = mapServiceInfo[serviceId]; + reward = sInfo.reward; - // Record service Ids - for (uint256 i = 0; i < size; ++i) { - serviceIds[i] = setServiceIds[i]; - } + // Add pending reward + reward += calculateServiceStakingLastReward(serviceId); } - /// @dev Checks if the service is staked. + /// @dev Gets the service staking state. /// @param serviceId. - /// @return isStaked True, if the service is staked. - function isServiceStaked(uint256 serviceId) external view returns (bool isStaked) { - isStaked = (mapServiceInfo[serviceId].tsStart > 0); + /// @return stakingState Staking state of the service. + function getServiceStakingState(uint256 serviceId) external view returns (ServiceStakingState stakingState) { + ServiceInfo memory sInfo = mapServiceInfo[serviceId]; + if (sInfo.inactivity > maxAllowedInactivity) { + stakingState = ServiceStakingState.Evicted; + } else if (sInfo.tsStart > 0) { + stakingState = ServiceStakingState.Staked; + } } /// @dev Gets the next reward checkpoint timestamp. @@ -951,4 +1109,23 @@ abstract contract ServiceStakingBase is ERC721TokenReceiver, IErrorsRegistries { // Last checkpoint timestamp plus the liveness period tsNext = tsCheckpoint + livenessPeriod; } + + /// @dev Gets staked service info. + /// @param serviceId Service Id. + /// @return sInfo Struct object with the corresponding service info. + function getServiceInfo(uint256 serviceId) external view returns (ServiceInfo memory sInfo) { + sInfo = mapServiceInfo[serviceId]; + } + + /// @dev Gets staked service Ids. + /// @return Staked service Ids. + function getServiceIds() public view returns (uint256[] memory) { + return setServiceIds; + } + + /// @dev Gets canonical agent Ids from the service configuration. + /// @return Agent Ids. + function getAgentIds() external view returns (uint256[] memory) { + return agentIds; + } } diff --git a/audits/internal4/analysis/slither_ServiceStakingBase-flatten.sol.ERC721.call-graph.png b/audits/internal4/analysis/slither_ServiceStakingBase-flatten.sol.ERC721.call-graph.png index 5c9205e4..5398b809 100644 Binary files a/audits/internal4/analysis/slither_ServiceStakingBase-flatten.sol.ERC721.call-graph.png and b/audits/internal4/analysis/slither_ServiceStakingBase-flatten.sol.ERC721.call-graph.png differ diff --git a/audits/internal4/analysis/slither_ServiceStakingBase-flatten.sol.ServiceStakingBase.call-graph.png b/audits/internal4/analysis/slither_ServiceStakingBase-flatten.sol.ServiceStakingBase.call-graph.png index 654e625a..d8e1993d 100644 Binary files a/audits/internal4/analysis/slither_ServiceStakingBase-flatten.sol.ServiceStakingBase.call-graph.png and b/audits/internal4/analysis/slither_ServiceStakingBase-flatten.sol.ServiceStakingBase.call-graph.png differ diff --git a/audits/internal4/analysis/slither_ServiceStakingBase-flatten.sol.all_contracts.call-graph.png b/audits/internal4/analysis/slither_ServiceStakingBase-flatten.sol.all_contracts.call-graph.png index 579438f6..ffdfd42e 100644 Binary files a/audits/internal4/analysis/slither_ServiceStakingBase-flatten.sol.all_contracts.call-graph.png and b/audits/internal4/analysis/slither_ServiceStakingBase-flatten.sol.all_contracts.call-graph.png differ diff --git a/audits/internal4/analysis/slither_ServiceStakingBase-flatten.sol.inheritance-graph.png b/audits/internal4/analysis/slither_ServiceStakingBase-flatten.sol.inheritance-graph.png index c368042d..1ee62916 100644 Binary files a/audits/internal4/analysis/slither_ServiceStakingBase-flatten.sol.inheritance-graph.png and b/audits/internal4/analysis/slither_ServiceStakingBase-flatten.sol.inheritance-graph.png differ diff --git a/audits/internal4/analysis/slither_full.txt b/audits/internal4/analysis/slither_full.txt index f1900b80..db1a28dd 100644 --- a/audits/internal4/analysis/slither_full.txt +++ b/audits/internal4/analysis/slither_full.txt @@ -1,283 +1,84 @@ -Notes: False positive. -INFO:Detectors: -ServiceStakingBase.calculateServiceStakingReward(uint256) (ServiceStakingNativeToken-flatten.sol#899-926) uses a dangerous strict equality: - - sInfo.tsStart == 0 (ServiceStakingNativeToken-flatten.sol#905) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-strict-equalities - -Notes: False positive. -INFO:Detectors: -ServiceStakingBase unstake parameter from is not related to msg.sender IService(serviceRegistry).safeTransferFrom(address(this),msg.sender,serviceId) (ServiceStakingNativeToken-flatten.sol#886) -Reference: https://ventral.digital/posts/2022/8/18/sznsdaos-bountyboard-unauthorized-transferfrom-vulnerability - -Notes: False positive. -INFO:Detectors: -Function ServiceStakingNativeToken._withdraw(address,uint256) (ServiceStakingNativeToken-flatten.sol#975-984) contains a low level call to a custom address -Reference: https://github.com/pessimistic-io/slitherin/blob/master/docs/call_forward_to_protected.md - -Notes: False positive. -INFO:Detectors: -Reentrancy in ServiceStakingBase.stake(uint256) (ServiceStakingNativeToken-flatten.sol#584-656): - External calls: - - IService(serviceRegistry).safeTransferFrom(msg.sender,address(this),serviceId) (ServiceStakingNativeToken-flatten.sol#653) - Event emitted after the call(s): - - ServiceStaked(serviceId,msg.sender,service.multisig,nonces) (ServiceStakingNativeToken-flatten.sol#655) -Reentrancy in ServiceStakingBase.unstake(uint256) (ServiceStakingNativeToken-flatten.sol#847-894): - External calls: - - IService(serviceRegistry).safeTransferFrom(address(this),msg.sender,serviceId) (ServiceStakingNativeToken-flatten.sol#886) - Event emitted after the call(s): - - ServiceUnstaked(serviceId,msg.sender,multisig,nonces,reward,tsStart) (ServiceStakingNativeToken-flatten.sol#893) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-3 - -Notes: False positive. -INFO:Detectors: -ServiceStakingBase._isRatioPass(uint256[],uint256[],uint256) (ServiceStakingNativeToken-flatten.sol#675-687) uses timestamp for comparisons - Dangerous comparisons: - - ts > 0 && curNonces[0] > lastNonces[0] (ServiceStakingNativeToken-flatten.sol#683) - - ratioPass = (ratio >= livenessRatio) (ServiceStakingNativeToken-flatten.sol#685) -ServiceStakingBase._calculateStakingRewards() (ServiceStakingNativeToken-flatten.sol#697-756) uses timestamp for comparisons - Dangerous comparisons: - - block.timestamp - tsCheckpointLast >= livenessPeriod && lastAvailableRewards > 0 (ServiceStakingNativeToken-flatten.sol#710) - - ts > serviceCheckpoint (ServiceStakingNativeToken-flatten.sol#736) -ServiceStakingBase.calculateServiceStakingReward(uint256) (ServiceStakingNativeToken-flatten.sol#899-926) uses timestamp for comparisons - Dangerous comparisons: - - sInfo.tsStart == 0 (ServiceStakingNativeToken-flatten.sol#905) -ServiceStakingBase.isServiceStaked(uint256) (ServiceStakingNativeToken-flatten.sol#944-946) uses timestamp for comparisons - Dangerous comparisons: - - isStaked = (mapServiceInfo[serviceId].tsStart > 0) (ServiceStakingNativeToken-flatten.sol#945) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#block-timestamp - -Notes: False positive. -INFO:Detectors: -ServiceStakingBase.stake(uint256) (ServiceStakingNativeToken-flatten.sol#584-656) has a high cyclomatic complexity (12). -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#cyclomatic-complexity - -Notes: False positive. -INFO:Detectors: -ERC721._burn(uint256) (ServiceStakingNativeToken-flatten.sol#296-311) is never used and should be removed -ERC721._mint(address,uint256) (ServiceStakingNativeToken-flatten.sol#281-294) is never used and should be removed -ERC721._safeMint(address,uint256) (ServiceStakingNativeToken-flatten.sol#317-326) is never used and should be removed -ERC721._safeMint(address,uint256,bytes) (ServiceStakingNativeToken-flatten.sol#328-341) is never used and should be removed -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#dead-code - -Notes: False positive. -INFO:Detectors: -Low level call in ServiceStakingNativeToken._withdraw(address,uint256) (ServiceStakingNativeToken-flatten.sol#975-984): - - (result) = to.call{value: amount}() (ServiceStakingNativeToken-flatten.sol#980) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#low-level-calls - -Notes: False positive. -INFO:Detectors: -Variable ERC721._ownerOf (ServiceStakingNativeToken-flatten.sol#155) is not in mixedCase -Variable ERC721._balanceOf (ServiceStakingNativeToken-flatten.sol#157) is not in mixedCase -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#conformance-to-solidity-naming-conventions - -Notes: False positive. -INFO:Detectors: -ERC721 (ServiceStakingNativeToken-flatten.sol#130-342) does not implement functions: - - ERC721.tokenURI(uint256) (ServiceStakingNativeToken-flatten.sol#149) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#unimplemented-functions -INFO:Detectors: - -Notes: False positive. -Manipulated call found: (success) = multisig.call(payload) (GnosisSafeSameAddressMultisig-flatten.sol#124) in GnosisSafeSameAddressMultisig.create(address[],uint256,bytes) (GnosisSafeSameAddressMultisig-flatten.sol#91-150) -Both destination and calldata could be manipulated - The call could be fully manipulated (arbitrary call) through GnosisSafeSameAddressMultisig.create(address[],uint256,bytes) (GnosisSafeSameAddressMultisig-flatten.sol#91-150) -Reference: https://github.com/pessimistic-io/slitherin/blob/master/docs/arbitrary_call.md - -Notes: False positive. -INFO:Detectors: -GnosisSafeSameAddressMultisig.create(address[],uint256,bytes).multisig (GnosisSafeSameAddressMultisig-flatten.sol#95) lacks a zero-check on : - - (success) = multisig.call(payload) (GnosisSafeSameAddressMultisig-flatten.sol#124) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#missing-zero-address-validation - -Notes: False positive. -INFO:Detectors: -GnosisSafeSameAddressMultisig.create(address[],uint256,bytes) (GnosisSafeSameAddressMultisig-flatten.sol#91-150) uses assembly - - INLINE ASM (GnosisSafeSameAddressMultisig-flatten.sol#104-106) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#assembly-usage - -Notes: False positive. -INFO:Detectors: -Low level call in GnosisSafeSameAddressMultisig.create(address[],uint256,bytes) (GnosisSafeSameAddressMultisig-flatten.sol#91-150): - - (success) = multisig.call(payload) (GnosisSafeSameAddressMultisig-flatten.sol#124) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#low-level-calls -Notes: False positive. -INFO:Detectors: -In a function GnosisSafeSameAddressMultisig.create(address[],uint256,bytes) (GnosisSafeSameAddressMultisig-flatten.sol#91-150) variable GnosisSafeSameAddressMultisig.DEFAULT_DATA_LENGTH (GnosisSafeSameAddressMultisig-flatten.sol#59) is read multiple times -Reference: https://github.com/pessimistic-io/slitherin/blob/master/docs/multiple_storage_read.md -INFO:Detectors: -IToken (ServiceStakingToken-flatten.sol#960-1003) has incorrect ERC721 function interface:IToken.approve(address,uint256) (ServiceStakingToken-flatten.sol#991) -IToken (ServiceStakingToken-flatten.sol#960-1003) has incorrect ERC721 function interface:IToken.transferFrom(address,address,uint256) (ServiceStakingToken-flatten.sol#998) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-erc721-interface +Notes: false positive +ServiceStakingBase.checkpoint().finalEligibleServiceRewards (ServiceStakingBase-flatten.sol#896) is a local variable never initialized +ServiceStakingBase.checkpoint().finalEligibleServiceIds (ServiceStakingBase-flatten.sol#895) is a local variable never initialized +ServiceStakingBase.unstake(uint256).idx (ServiceStakingBase-flatten.sol#1029) is a local variable never initialized +ServiceStakingBase._evict(uint256[],uint256[],uint256).sCounter (ServiceStakingBase-flatten.sol#834) is a local variable never initialized +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#uninitialized-local-variables -Notes: False positive. +Notes: false positive INFO:Detectors: -ServiceStakingBase.calculateServiceStakingReward(uint256) (ServiceStakingToken-flatten.sol#900-927) uses a dangerous strict equality: - - sInfo.tsStart == 0 (ServiceStakingToken-flatten.sol#906) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-strict-equalities -INFO:Detectors: -ServiceStakingBase unstake parameter from is not related to msg.sender IService(serviceRegistry).safeTransferFrom(address(this),msg.sender,serviceId) (ServiceStakingToken-flatten.sol#887) +ServiceStakingBase unstake parameter from is not related to msg.sender IService(serviceRegistry).safeTransferFrom(address(this),msg.sender,serviceId) (ServiceStakingBase-flatten.sol#1059) Reference: https://ventral.digital/posts/2022/8/18/sznsdaos-bountyboard-unauthorized-transferfrom-vulnerability -Notes: False positive. +Notes: false positive INFO:Detectors: -Reentrancy in ServiceStakingBase.stake(uint256) (ServiceStakingToken-flatten.sol#585-657): +Reentrancy in ServiceStakingBase.stake(uint256) (ServiceStakingBase-flatten.sol#628-706): External calls: - - IService(serviceRegistry).safeTransferFrom(msg.sender,address(this),serviceId) (ServiceStakingToken-flatten.sol#654) + - IService(serviceRegistry).safeTransferFrom(msg.sender,address(this),serviceId) (ServiceStakingBase-flatten.sol#703) Event emitted after the call(s): - - ServiceStaked(serviceId,msg.sender,service.multisig,nonces) (ServiceStakingToken-flatten.sol#656) -Reentrancy in ServiceStakingBase.unstake(uint256) (ServiceStakingToken-flatten.sol#848-895): + - ServiceStaked(epochCounter,serviceId,msg.sender,service.multisig,nonces) (ServiceStakingBase-flatten.sol#705) +Reentrancy in ServiceStakingBase.unstake(uint256) (ServiceStakingBase-flatten.sol#1001-1067): External calls: - - IService(serviceRegistry).safeTransferFrom(address(this),msg.sender,serviceId) (ServiceStakingToken-flatten.sol#887) + - IService(serviceRegistry).safeTransferFrom(address(this),msg.sender,serviceId) (ServiceStakingBase-flatten.sol#1059) Event emitted after the call(s): - - ServiceUnstaked(serviceId,msg.sender,multisig,nonces,reward,tsStart) (ServiceStakingToken-flatten.sol#894) + - ServiceUnstaked(epochCounter,serviceId,msg.sender,multisig,nonces,reward) (ServiceStakingBase-flatten.sol#1066) Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-3 -Notes: False positive. +Notes: false positive INFO:Detectors: -ServiceStakingBase._isRatioPass(uint256[],uint256[],uint256) (ServiceStakingToken-flatten.sol#676-688) uses timestamp for comparisons - Dangerous comparisons: - - ts > 0 && curNonces[0] > lastNonces[0] (ServiceStakingToken-flatten.sol#684) - - ratioPass = (ratio >= livenessRatio) (ServiceStakingToken-flatten.sol#686) -ServiceStakingBase._calculateStakingRewards() (ServiceStakingToken-flatten.sol#698-757) uses timestamp for comparisons - Dangerous comparisons: - - block.timestamp - tsCheckpointLast >= livenessPeriod && lastAvailableRewards > 0 (ServiceStakingToken-flatten.sol#711) - - ts > serviceCheckpoint (ServiceStakingToken-flatten.sol#737) -ServiceStakingBase.calculateServiceStakingReward(uint256) (ServiceStakingToken-flatten.sol#900-927) uses timestamp for comparisons +ServiceStakingBase._isRatioPass(uint256[],uint256[],uint256) (ServiceStakingBase-flatten.sol#725-737) uses timestamp for comparisons Dangerous comparisons: - - sInfo.tsStart == 0 (ServiceStakingToken-flatten.sol#906) -ServiceStakingBase.isServiceStaked(uint256) (ServiceStakingToken-flatten.sol#945-947) uses timestamp for comparisons - Dangerous comparisons: - - isStaked = (mapServiceInfo[serviceId].tsStart > 0) (ServiceStakingToken-flatten.sol#946) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#block-timestamp - -Notes: False positive. -INFO:Detectors: -SafeTransferLib.safeTransferFrom(address,address,address,uint256) (ServiceStakingToken-flatten.sol#1026-1057) uses assembly - - INLINE ASM (ServiceStakingToken-flatten.sol#1030-1052) -SafeTransferLib.safeTransfer(address,address,uint256) (ServiceStakingToken-flatten.sol#1068-1098) uses assembly - - INLINE ASM (ServiceStakingToken-flatten.sol#1072-1093) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#assembly-usage - -Notes: False positive. -INFO:Detectors: -ServiceStakingBase.stake(uint256) (ServiceStakingToken-flatten.sol#585-657) has a high cyclomatic complexity (12). -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#cyclomatic-complexity - -Notes: False positive. -INFO:Detectors: -ERC721._burn(uint256) (ServiceStakingToken-flatten.sol#296-311) is never used and should be removed -ERC721._mint(address,uint256) (ServiceStakingToken-flatten.sol#281-294) is never used and should be removed -ERC721._safeMint(address,uint256) (ServiceStakingToken-flatten.sol#317-326) is never used and should be removed -ERC721._safeMint(address,uint256,bytes) (ServiceStakingToken-flatten.sol#328-341) is never used and should be removed -ServiceStakingBase._checkTokenStakingDeposit(uint256,uint256) (ServiceStakingToken-flatten.sol#571-576) is never used and should be removed -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#dead-code - -Notes: False positive. -INFO:Detectors: -Variable ERC721._ownerOf (ServiceStakingToken-flatten.sol#155) is not in mixedCase -Variable ERC721._balanceOf (ServiceStakingToken-flatten.sol#157) is not in mixedCase -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#conformance-to-solidity-naming-conventions - -Notes: False positive. -INFO:Detectors: -SafeTransferLib.safeTransferFrom(address,address,address,uint256) (ServiceStakingToken-flatten.sol#1026-1057) uses literals with too many digits: - - mstore(uint256,uint256)(0,0x23b872dd00000000000000000000000000000000000000000000000000000000) (ServiceStakingToken-flatten.sol#1035) -SafeTransferLib.safeTransfer(address,address,uint256) (ServiceStakingToken-flatten.sol#1068-1098) uses literals with too many digits: - - mstore(uint256,uint256)(0,0xa9059cbb00000000000000000000000000000000000000000000000000000000) (ServiceStakingToken-flatten.sol#1077) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#too-many-digits - -Notes: False positive. -INFO:Detectors: -ERC721 (ServiceStakingToken-flatten.sol#130-342) does not implement functions: - - ERC721.tokenURI(uint256) (ServiceStakingToken-flatten.sol#149) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#unimplemented-functions - -Notes: False positive. -INFO:Detectors: -Function SafeTransferLib.safeTransferFrom(address,address,address,uint256) (ServiceStakingToken-flatten.sol#1026-1057) contains magic numbers: 4, 36, 68 -Function SafeTransferLib.safeTransfer(address,address,uint256) (ServiceStakingToken-flatten.sol#1068-1098) contains magic numbers: 4, 36 -Reference: https://github.com/pessimistic-io/slitherin/blob/master/docs/magic_number.md - -Notes: False positive. -INFO:Detectors: -ServiceStakingBase.calculateServiceStakingReward(uint256) (ServiceStakingBase-flatten.sol#899-926) uses a dangerous strict equality: - - sInfo.tsStart == 0 (ServiceStakingBase-flatten.sol#905) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-strict-equalities - -Notes: False positive. -INFO:Detectors: -ServiceStakingBase unstake parameter from is not related to msg.sender IService(serviceRegistry).safeTransferFrom(address(this),msg.sender,serviceId) (ServiceStakingBase-flatten.sol#886) -Reference: https://ventral.digital/posts/2022/8/18/sznsdaos-bountyboard-unauthorized-transferfrom-vulnerability - -Notes: False positive. -INFO:Detectors: -Reentrancy in ServiceStakingBase.stake(uint256) (ServiceStakingBase-flatten.sol#584-656): - External calls: - - IService(serviceRegistry).safeTransferFrom(msg.sender,address(this),serviceId) (ServiceStakingBase-flatten.sol#653) - Event emitted after the call(s): - - ServiceStaked(serviceId,msg.sender,service.multisig,nonces) (ServiceStakingBase-flatten.sol#655) -Reentrancy in ServiceStakingBase.unstake(uint256) (ServiceStakingBase-flatten.sol#847-894): - External calls: - - IService(serviceRegistry).safeTransferFrom(address(this),msg.sender,serviceId) (ServiceStakingBase-flatten.sol#886) - Event emitted after the call(s): - - ServiceUnstaked(serviceId,msg.sender,multisig,nonces,reward,tsStart) (ServiceStakingBase-flatten.sol#893) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-3 - -Notes: False positive. -INFO:Detectors: -ServiceStakingBase._isRatioPass(uint256[],uint256[],uint256) (ServiceStakingBase-flatten.sol#675-687) uses timestamp for comparisons + - ts > 0 && curNonces[0] > lastNonces[0] (ServiceStakingBase-flatten.sol#733) + - ratioPass = (ratio >= livenessRatio) (ServiceStakingBase-flatten.sol#735) +ServiceStakingBase._calculateStakingRewards() (ServiceStakingBase-flatten.sol#748-810) uses timestamp for comparisons Dangerous comparisons: - - ts > 0 && curNonces[0] > lastNonces[0] (ServiceStakingBase-flatten.sol#683) - - ratioPass = (ratio >= livenessRatio) (ServiceStakingBase-flatten.sol#685) -ServiceStakingBase._calculateStakingRewards() (ServiceStakingBase-flatten.sol#697-756) uses timestamp for comparisons + - block.timestamp - tsCheckpointLast >= livenessPeriod && lastAvailableRewards > 0 (ServiceStakingBase-flatten.sol#762) + - ts > serviceCheckpoint (ServiceStakingBase-flatten.sol#789) +ServiceStakingBase.checkpoint() (ServiceStakingBase-flatten.sol#880-997) uses timestamp for comparisons Dangerous comparisons: - - block.timestamp - tsCheckpointLast >= livenessPeriod && lastAvailableRewards > 0 (ServiceStakingBase-flatten.sol#710) - - ts > serviceCheckpoint (ServiceStakingBase-flatten.sol#736) -ServiceStakingBase.calculateServiceStakingReward(uint256) (ServiceStakingBase-flatten.sol#899-926) uses timestamp for comparisons + - serviceInactivity[i_scope_1] > maxAllowedInactivity (ServiceStakingBase-flatten.sol#969) +ServiceStakingBase.unstake(uint256) (ServiceStakingBase-flatten.sol#1001-1067) uses timestamp for comparisons Dangerous comparisons: - - sInfo.tsStart == 0 (ServiceStakingBase-flatten.sol#905) -ServiceStakingBase.isServiceStaked(uint256) (ServiceStakingBase-flatten.sol#944-946) uses timestamp for comparisons + - ts <= maxAllowedInactivity && availableRewards > 0 (ServiceStakingBase-flatten.sol#1014) +ServiceStakingBase.getServiceStakingState(uint256) (ServiceStakingBase-flatten.sol#1107-1114) uses timestamp for comparisons Dangerous comparisons: - - isStaked = (mapServiceInfo[serviceId].tsStart > 0) (ServiceStakingBase-flatten.sol#945) + - sInfo.inactivity > maxAllowedInactivity (ServiceStakingBase-flatten.sol#1109) + - sInfo.tsStart > 0 (ServiceStakingBase-flatten.sol#1111) Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#block-timestamp -Notes: False positive. +Notes: false positive INFO:Detectors: -ServiceStakingBase.stake(uint256) (ServiceStakingBase-flatten.sol#584-656) has a high cyclomatic complexity (12). +ServiceStakingBase.stake(uint256) (ServiceStakingBase-flatten.sol#628-706) has a high cyclomatic complexity (13). Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#cyclomatic-complexity - -Notes: False positive. INFO:Detectors: -ERC721._burn(uint256) (ServiceStakingBase-flatten.sol#296-311) is never used and should be removed -ERC721._mint(address,uint256) (ServiceStakingBase-flatten.sol#281-294) is never used and should be removed -ERC721._safeMint(address,uint256) (ServiceStakingBase-flatten.sol#317-326) is never used and should be removed -ERC721._safeMint(address,uint256,bytes) (ServiceStakingBase-flatten.sol#328-341) is never used and should be removed +ERC721._burn(uint256) (ServiceStakingBase-flatten.sol#300-315) is never used and should be removed +ERC721._mint(address,uint256) (ServiceStakingBase-flatten.sol#285-298) is never used and should be removed +ERC721._safeMint(address,uint256) (ServiceStakingBase-flatten.sol#321-330) is never used and should be removed +ERC721._safeMint(address,uint256,bytes) (ServiceStakingBase-flatten.sol#332-345) is never used and should be removed Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#dead-code -Notes: False positive. +Notes: false positive INFO:Detectors: -Variable ERC721._ownerOf (ServiceStakingBase-flatten.sol#155) is not in mixedCase -Variable ERC721._balanceOf (ServiceStakingBase-flatten.sol#157) is not in mixedCase +Variable ERC721._ownerOf (ServiceStakingBase-flatten.sol#159) is not in mixedCase +Variable ERC721._balanceOf (ServiceStakingBase-flatten.sol#161) is not in mixedCase Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#conformance-to-solidity-naming-conventions -Notes: False positive. +Notes: false positive INFO:Detectors: -ERC721 (ServiceStakingBase-flatten.sol#130-342) does not implement functions: - - ERC721.tokenURI(uint256) (ServiceStakingBase-flatten.sol#149) -ServiceStakingBase (ServiceStakingBase-flatten.sol#447-954) does not implement functions: - - ServiceStakingBase._withdraw(address,uint256) (ServiceStakingBase-flatten.sol#580) +ERC721 (ServiceStakingBase-flatten.sol#134-346) does not implement functions: + - ERC721.tokenURI(uint256) (ServiceStakingBase-flatten.sol#153) +ServiceStakingBase (ServiceStakingBase-flatten.sol#468-1141) does not implement functions: + - ServiceStakingBase._withdraw(address,uint256) (ServiceStakingBase-flatten.sol#622) Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#unimplemented-functions -Notes: False positive. +Notes: false positive INFO:Detectors: -ServiceStakingBase.balance (ServiceStakingBase-flatten.sol#502) should be constant +ServiceStakingBase.balance (ServiceStakingBase-flatten.sol#540) should be constant Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#state-variables-that-could-be-declared-constant - -Notes: False positive. INFO:Detectors: -In a function ServiceStakingBase.stake(uint256) (ServiceStakingBase-flatten.sol#584-656) variable ServiceStakingBase.agentIds (ServiceStakingBase-flatten.sol#508) is read multiple times -In a function ServiceStakingBase.checkpoint() (ServiceStakingBase-flatten.sol#765-843) variable ServiceStakingBase.mapServiceInfo (ServiceStakingBase-flatten.sol#510) is read multiple times +In a function ServiceStakingBase.stake(uint256) (ServiceStakingBase-flatten.sol#628-706) variable ServiceStakingBase.agentIds (ServiceStakingBase-flatten.sol#546) is read multiple times +In a function ServiceStakingBase._evict(uint256[],uint256[],uint256) (ServiceStakingBase-flatten.sol#816-872) variable ServiceStakingBase.setServiceIds (ServiceStakingBase-flatten.sol#550) is read multiple times +In a function ServiceStakingBase.checkpoint() (ServiceStakingBase-flatten.sol#880-997) variable ServiceStakingBase.mapServiceInfo (ServiceStakingBase-flatten.sol#548) is read multiple times Reference: https://github.com/pessimistic-io/slitherin/blob/master/docs/multiple_storage_read.md -INFO:Slither:. analyzed (25 contracts with 108 detectors), 76 result(s) found +INFO:Slither:. analyzed (6 contracts with 108 detectors), 30 result(s) found diff --git a/audits/internal4/analysis/slither_full_25_10_23.txt b/audits/internal4/analysis/slither_full_25_10_23.txt new file mode 100644 index 00000000..f1900b80 --- /dev/null +++ b/audits/internal4/analysis/slither_full_25_10_23.txt @@ -0,0 +1,283 @@ +Notes: False positive. +INFO:Detectors: +ServiceStakingBase.calculateServiceStakingReward(uint256) (ServiceStakingNativeToken-flatten.sol#899-926) uses a dangerous strict equality: + - sInfo.tsStart == 0 (ServiceStakingNativeToken-flatten.sol#905) +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-strict-equalities + +Notes: False positive. +INFO:Detectors: +ServiceStakingBase unstake parameter from is not related to msg.sender IService(serviceRegistry).safeTransferFrom(address(this),msg.sender,serviceId) (ServiceStakingNativeToken-flatten.sol#886) +Reference: https://ventral.digital/posts/2022/8/18/sznsdaos-bountyboard-unauthorized-transferfrom-vulnerability + +Notes: False positive. +INFO:Detectors: +Function ServiceStakingNativeToken._withdraw(address,uint256) (ServiceStakingNativeToken-flatten.sol#975-984) contains a low level call to a custom address +Reference: https://github.com/pessimistic-io/slitherin/blob/master/docs/call_forward_to_protected.md + +Notes: False positive. +INFO:Detectors: +Reentrancy in ServiceStakingBase.stake(uint256) (ServiceStakingNativeToken-flatten.sol#584-656): + External calls: + - IService(serviceRegistry).safeTransferFrom(msg.sender,address(this),serviceId) (ServiceStakingNativeToken-flatten.sol#653) + Event emitted after the call(s): + - ServiceStaked(serviceId,msg.sender,service.multisig,nonces) (ServiceStakingNativeToken-flatten.sol#655) +Reentrancy in ServiceStakingBase.unstake(uint256) (ServiceStakingNativeToken-flatten.sol#847-894): + External calls: + - IService(serviceRegistry).safeTransferFrom(address(this),msg.sender,serviceId) (ServiceStakingNativeToken-flatten.sol#886) + Event emitted after the call(s): + - ServiceUnstaked(serviceId,msg.sender,multisig,nonces,reward,tsStart) (ServiceStakingNativeToken-flatten.sol#893) +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-3 + +Notes: False positive. +INFO:Detectors: +ServiceStakingBase._isRatioPass(uint256[],uint256[],uint256) (ServiceStakingNativeToken-flatten.sol#675-687) uses timestamp for comparisons + Dangerous comparisons: + - ts > 0 && curNonces[0] > lastNonces[0] (ServiceStakingNativeToken-flatten.sol#683) + - ratioPass = (ratio >= livenessRatio) (ServiceStakingNativeToken-flatten.sol#685) +ServiceStakingBase._calculateStakingRewards() (ServiceStakingNativeToken-flatten.sol#697-756) uses timestamp for comparisons + Dangerous comparisons: + - block.timestamp - tsCheckpointLast >= livenessPeriod && lastAvailableRewards > 0 (ServiceStakingNativeToken-flatten.sol#710) + - ts > serviceCheckpoint (ServiceStakingNativeToken-flatten.sol#736) +ServiceStakingBase.calculateServiceStakingReward(uint256) (ServiceStakingNativeToken-flatten.sol#899-926) uses timestamp for comparisons + Dangerous comparisons: + - sInfo.tsStart == 0 (ServiceStakingNativeToken-flatten.sol#905) +ServiceStakingBase.isServiceStaked(uint256) (ServiceStakingNativeToken-flatten.sol#944-946) uses timestamp for comparisons + Dangerous comparisons: + - isStaked = (mapServiceInfo[serviceId].tsStart > 0) (ServiceStakingNativeToken-flatten.sol#945) +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#block-timestamp + +Notes: False positive. +INFO:Detectors: +ServiceStakingBase.stake(uint256) (ServiceStakingNativeToken-flatten.sol#584-656) has a high cyclomatic complexity (12). +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#cyclomatic-complexity + +Notes: False positive. +INFO:Detectors: +ERC721._burn(uint256) (ServiceStakingNativeToken-flatten.sol#296-311) is never used and should be removed +ERC721._mint(address,uint256) (ServiceStakingNativeToken-flatten.sol#281-294) is never used and should be removed +ERC721._safeMint(address,uint256) (ServiceStakingNativeToken-flatten.sol#317-326) is never used and should be removed +ERC721._safeMint(address,uint256,bytes) (ServiceStakingNativeToken-flatten.sol#328-341) is never used and should be removed +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#dead-code + +Notes: False positive. +INFO:Detectors: +Low level call in ServiceStakingNativeToken._withdraw(address,uint256) (ServiceStakingNativeToken-flatten.sol#975-984): + - (result) = to.call{value: amount}() (ServiceStakingNativeToken-flatten.sol#980) +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#low-level-calls + +Notes: False positive. +INFO:Detectors: +Variable ERC721._ownerOf (ServiceStakingNativeToken-flatten.sol#155) is not in mixedCase +Variable ERC721._balanceOf (ServiceStakingNativeToken-flatten.sol#157) is not in mixedCase +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#conformance-to-solidity-naming-conventions + +Notes: False positive. +INFO:Detectors: +ERC721 (ServiceStakingNativeToken-flatten.sol#130-342) does not implement functions: + - ERC721.tokenURI(uint256) (ServiceStakingNativeToken-flatten.sol#149) +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#unimplemented-functions +INFO:Detectors: + +Notes: False positive. +Manipulated call found: (success) = multisig.call(payload) (GnosisSafeSameAddressMultisig-flatten.sol#124) in GnosisSafeSameAddressMultisig.create(address[],uint256,bytes) (GnosisSafeSameAddressMultisig-flatten.sol#91-150) +Both destination and calldata could be manipulated + The call could be fully manipulated (arbitrary call) through GnosisSafeSameAddressMultisig.create(address[],uint256,bytes) (GnosisSafeSameAddressMultisig-flatten.sol#91-150) +Reference: https://github.com/pessimistic-io/slitherin/blob/master/docs/arbitrary_call.md + +Notes: False positive. +INFO:Detectors: +GnosisSafeSameAddressMultisig.create(address[],uint256,bytes).multisig (GnosisSafeSameAddressMultisig-flatten.sol#95) lacks a zero-check on : + - (success) = multisig.call(payload) (GnosisSafeSameAddressMultisig-flatten.sol#124) +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#missing-zero-address-validation + +Notes: False positive. +INFO:Detectors: +GnosisSafeSameAddressMultisig.create(address[],uint256,bytes) (GnosisSafeSameAddressMultisig-flatten.sol#91-150) uses assembly + - INLINE ASM (GnosisSafeSameAddressMultisig-flatten.sol#104-106) +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#assembly-usage + +Notes: False positive. +INFO:Detectors: +Low level call in GnosisSafeSameAddressMultisig.create(address[],uint256,bytes) (GnosisSafeSameAddressMultisig-flatten.sol#91-150): + - (success) = multisig.call(payload) (GnosisSafeSameAddressMultisig-flatten.sol#124) +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#low-level-calls + +Notes: False positive. +INFO:Detectors: +In a function GnosisSafeSameAddressMultisig.create(address[],uint256,bytes) (GnosisSafeSameAddressMultisig-flatten.sol#91-150) variable GnosisSafeSameAddressMultisig.DEFAULT_DATA_LENGTH (GnosisSafeSameAddressMultisig-flatten.sol#59) is read multiple times +Reference: https://github.com/pessimistic-io/slitherin/blob/master/docs/multiple_storage_read.md +INFO:Detectors: +IToken (ServiceStakingToken-flatten.sol#960-1003) has incorrect ERC721 function interface:IToken.approve(address,uint256) (ServiceStakingToken-flatten.sol#991) +IToken (ServiceStakingToken-flatten.sol#960-1003) has incorrect ERC721 function interface:IToken.transferFrom(address,address,uint256) (ServiceStakingToken-flatten.sol#998) +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-erc721-interface + +Notes: False positive. +INFO:Detectors: +ServiceStakingBase.calculateServiceStakingReward(uint256) (ServiceStakingToken-flatten.sol#900-927) uses a dangerous strict equality: + - sInfo.tsStart == 0 (ServiceStakingToken-flatten.sol#906) +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-strict-equalities +INFO:Detectors: +ServiceStakingBase unstake parameter from is not related to msg.sender IService(serviceRegistry).safeTransferFrom(address(this),msg.sender,serviceId) (ServiceStakingToken-flatten.sol#887) +Reference: https://ventral.digital/posts/2022/8/18/sznsdaos-bountyboard-unauthorized-transferfrom-vulnerability + +Notes: False positive. +INFO:Detectors: +Reentrancy in ServiceStakingBase.stake(uint256) (ServiceStakingToken-flatten.sol#585-657): + External calls: + - IService(serviceRegistry).safeTransferFrom(msg.sender,address(this),serviceId) (ServiceStakingToken-flatten.sol#654) + Event emitted after the call(s): + - ServiceStaked(serviceId,msg.sender,service.multisig,nonces) (ServiceStakingToken-flatten.sol#656) +Reentrancy in ServiceStakingBase.unstake(uint256) (ServiceStakingToken-flatten.sol#848-895): + External calls: + - IService(serviceRegistry).safeTransferFrom(address(this),msg.sender,serviceId) (ServiceStakingToken-flatten.sol#887) + Event emitted after the call(s): + - ServiceUnstaked(serviceId,msg.sender,multisig,nonces,reward,tsStart) (ServiceStakingToken-flatten.sol#894) +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-3 + +Notes: False positive. +INFO:Detectors: +ServiceStakingBase._isRatioPass(uint256[],uint256[],uint256) (ServiceStakingToken-flatten.sol#676-688) uses timestamp for comparisons + Dangerous comparisons: + - ts > 0 && curNonces[0] > lastNonces[0] (ServiceStakingToken-flatten.sol#684) + - ratioPass = (ratio >= livenessRatio) (ServiceStakingToken-flatten.sol#686) +ServiceStakingBase._calculateStakingRewards() (ServiceStakingToken-flatten.sol#698-757) uses timestamp for comparisons + Dangerous comparisons: + - block.timestamp - tsCheckpointLast >= livenessPeriod && lastAvailableRewards > 0 (ServiceStakingToken-flatten.sol#711) + - ts > serviceCheckpoint (ServiceStakingToken-flatten.sol#737) +ServiceStakingBase.calculateServiceStakingReward(uint256) (ServiceStakingToken-flatten.sol#900-927) uses timestamp for comparisons + Dangerous comparisons: + - sInfo.tsStart == 0 (ServiceStakingToken-flatten.sol#906) +ServiceStakingBase.isServiceStaked(uint256) (ServiceStakingToken-flatten.sol#945-947) uses timestamp for comparisons + Dangerous comparisons: + - isStaked = (mapServiceInfo[serviceId].tsStart > 0) (ServiceStakingToken-flatten.sol#946) +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#block-timestamp + +Notes: False positive. +INFO:Detectors: +SafeTransferLib.safeTransferFrom(address,address,address,uint256) (ServiceStakingToken-flatten.sol#1026-1057) uses assembly + - INLINE ASM (ServiceStakingToken-flatten.sol#1030-1052) +SafeTransferLib.safeTransfer(address,address,uint256) (ServiceStakingToken-flatten.sol#1068-1098) uses assembly + - INLINE ASM (ServiceStakingToken-flatten.sol#1072-1093) +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#assembly-usage + +Notes: False positive. +INFO:Detectors: +ServiceStakingBase.stake(uint256) (ServiceStakingToken-flatten.sol#585-657) has a high cyclomatic complexity (12). +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#cyclomatic-complexity + +Notes: False positive. +INFO:Detectors: +ERC721._burn(uint256) (ServiceStakingToken-flatten.sol#296-311) is never used and should be removed +ERC721._mint(address,uint256) (ServiceStakingToken-flatten.sol#281-294) is never used and should be removed +ERC721._safeMint(address,uint256) (ServiceStakingToken-flatten.sol#317-326) is never used and should be removed +ERC721._safeMint(address,uint256,bytes) (ServiceStakingToken-flatten.sol#328-341) is never used and should be removed +ServiceStakingBase._checkTokenStakingDeposit(uint256,uint256) (ServiceStakingToken-flatten.sol#571-576) is never used and should be removed +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#dead-code + +Notes: False positive. +INFO:Detectors: +Variable ERC721._ownerOf (ServiceStakingToken-flatten.sol#155) is not in mixedCase +Variable ERC721._balanceOf (ServiceStakingToken-flatten.sol#157) is not in mixedCase +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#conformance-to-solidity-naming-conventions + +Notes: False positive. +INFO:Detectors: +SafeTransferLib.safeTransferFrom(address,address,address,uint256) (ServiceStakingToken-flatten.sol#1026-1057) uses literals with too many digits: + - mstore(uint256,uint256)(0,0x23b872dd00000000000000000000000000000000000000000000000000000000) (ServiceStakingToken-flatten.sol#1035) +SafeTransferLib.safeTransfer(address,address,uint256) (ServiceStakingToken-flatten.sol#1068-1098) uses literals with too many digits: + - mstore(uint256,uint256)(0,0xa9059cbb00000000000000000000000000000000000000000000000000000000) (ServiceStakingToken-flatten.sol#1077) +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#too-many-digits + +Notes: False positive. +INFO:Detectors: +ERC721 (ServiceStakingToken-flatten.sol#130-342) does not implement functions: + - ERC721.tokenURI(uint256) (ServiceStakingToken-flatten.sol#149) +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#unimplemented-functions + +Notes: False positive. +INFO:Detectors: +Function SafeTransferLib.safeTransferFrom(address,address,address,uint256) (ServiceStakingToken-flatten.sol#1026-1057) contains magic numbers: 4, 36, 68 +Function SafeTransferLib.safeTransfer(address,address,uint256) (ServiceStakingToken-flatten.sol#1068-1098) contains magic numbers: 4, 36 +Reference: https://github.com/pessimistic-io/slitherin/blob/master/docs/magic_number.md + +Notes: False positive. +INFO:Detectors: +ServiceStakingBase.calculateServiceStakingReward(uint256) (ServiceStakingBase-flatten.sol#899-926) uses a dangerous strict equality: + - sInfo.tsStart == 0 (ServiceStakingBase-flatten.sol#905) +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-strict-equalities + +Notes: False positive. +INFO:Detectors: +ServiceStakingBase unstake parameter from is not related to msg.sender IService(serviceRegistry).safeTransferFrom(address(this),msg.sender,serviceId) (ServiceStakingBase-flatten.sol#886) +Reference: https://ventral.digital/posts/2022/8/18/sznsdaos-bountyboard-unauthorized-transferfrom-vulnerability + +Notes: False positive. +INFO:Detectors: +Reentrancy in ServiceStakingBase.stake(uint256) (ServiceStakingBase-flatten.sol#584-656): + External calls: + - IService(serviceRegistry).safeTransferFrom(msg.sender,address(this),serviceId) (ServiceStakingBase-flatten.sol#653) + Event emitted after the call(s): + - ServiceStaked(serviceId,msg.sender,service.multisig,nonces) (ServiceStakingBase-flatten.sol#655) +Reentrancy in ServiceStakingBase.unstake(uint256) (ServiceStakingBase-flatten.sol#847-894): + External calls: + - IService(serviceRegistry).safeTransferFrom(address(this),msg.sender,serviceId) (ServiceStakingBase-flatten.sol#886) + Event emitted after the call(s): + - ServiceUnstaked(serviceId,msg.sender,multisig,nonces,reward,tsStart) (ServiceStakingBase-flatten.sol#893) +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-3 + +Notes: False positive. +INFO:Detectors: +ServiceStakingBase._isRatioPass(uint256[],uint256[],uint256) (ServiceStakingBase-flatten.sol#675-687) uses timestamp for comparisons + Dangerous comparisons: + - ts > 0 && curNonces[0] > lastNonces[0] (ServiceStakingBase-flatten.sol#683) + - ratioPass = (ratio >= livenessRatio) (ServiceStakingBase-flatten.sol#685) +ServiceStakingBase._calculateStakingRewards() (ServiceStakingBase-flatten.sol#697-756) uses timestamp for comparisons + Dangerous comparisons: + - block.timestamp - tsCheckpointLast >= livenessPeriod && lastAvailableRewards > 0 (ServiceStakingBase-flatten.sol#710) + - ts > serviceCheckpoint (ServiceStakingBase-flatten.sol#736) +ServiceStakingBase.calculateServiceStakingReward(uint256) (ServiceStakingBase-flatten.sol#899-926) uses timestamp for comparisons + Dangerous comparisons: + - sInfo.tsStart == 0 (ServiceStakingBase-flatten.sol#905) +ServiceStakingBase.isServiceStaked(uint256) (ServiceStakingBase-flatten.sol#944-946) uses timestamp for comparisons + Dangerous comparisons: + - isStaked = (mapServiceInfo[serviceId].tsStart > 0) (ServiceStakingBase-flatten.sol#945) +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#block-timestamp + +Notes: False positive. +INFO:Detectors: +ServiceStakingBase.stake(uint256) (ServiceStakingBase-flatten.sol#584-656) has a high cyclomatic complexity (12). +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#cyclomatic-complexity + +Notes: False positive. +INFO:Detectors: +ERC721._burn(uint256) (ServiceStakingBase-flatten.sol#296-311) is never used and should be removed +ERC721._mint(address,uint256) (ServiceStakingBase-flatten.sol#281-294) is never used and should be removed +ERC721._safeMint(address,uint256) (ServiceStakingBase-flatten.sol#317-326) is never used and should be removed +ERC721._safeMint(address,uint256,bytes) (ServiceStakingBase-flatten.sol#328-341) is never used and should be removed +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#dead-code + +Notes: False positive. +INFO:Detectors: +Variable ERC721._ownerOf (ServiceStakingBase-flatten.sol#155) is not in mixedCase +Variable ERC721._balanceOf (ServiceStakingBase-flatten.sol#157) is not in mixedCase +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#conformance-to-solidity-naming-conventions + +Notes: False positive. +INFO:Detectors: +ERC721 (ServiceStakingBase-flatten.sol#130-342) does not implement functions: + - ERC721.tokenURI(uint256) (ServiceStakingBase-flatten.sol#149) +ServiceStakingBase (ServiceStakingBase-flatten.sol#447-954) does not implement functions: + - ServiceStakingBase._withdraw(address,uint256) (ServiceStakingBase-flatten.sol#580) +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#unimplemented-functions + +Notes: False positive. +INFO:Detectors: +ServiceStakingBase.balance (ServiceStakingBase-flatten.sol#502) should be constant +Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#state-variables-that-could-be-declared-constant + +Notes: False positive. +INFO:Detectors: +In a function ServiceStakingBase.stake(uint256) (ServiceStakingBase-flatten.sol#584-656) variable ServiceStakingBase.agentIds (ServiceStakingBase-flatten.sol#508) is read multiple times +In a function ServiceStakingBase.checkpoint() (ServiceStakingBase-flatten.sol#765-843) variable ServiceStakingBase.mapServiceInfo (ServiceStakingBase-flatten.sol#510) is read multiple times +Reference: https://github.com/pessimistic-io/slitherin/blob/master/docs/multiple_storage_read.md +INFO:Slither:. analyzed (25 contracts with 108 detectors), 76 result(s) found