Platform: Code4rena
Start Date: 08/03/2023
Pot Size: $60,500 USDC
Total HM: 2
Participants: 123
Period: 7 days
Judge: hansfriese
Id: 220
League: ETH
Rank: 19/123
Findings: 1
Award: $235.24
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0xSmartContract
Also found by: 0x1f8b, 0x6980, 0xAgro, 0xSolus, 0xhacksmithh, 0xkazim, ABA, BPZ, BowTiedOriole, ChainReview, DadeKuma, DeFiHackLabs, Deathstore, DevABDee, Diana, Dravee, Dug, Englave, Go-Langer, Haipls, IceBear, Inspex, Jeiwan, Kek, Kresh, Madalad, MatricksDeCoder, MyFDsYours, RaymondFam, Rolezn, SAAJ, Sathish9098, Taloner, Udsen, Viktor_Cortess, atharvasama, ayden, brgltd, btk, carlitox477, catellatech, chaduke, codeislight, deadrxsezzz, descharre, erictee, fatherOfBlocks, favelanky, glcanvas, handsomegiraffe, jasonxiale, jekapi, joestakey, lemonr, luxartvinsec, martin, matrix_0wl, minhquanym, mrpathfindr, nadin, oyc_109, parsely, peanuts, pfedprog, rbserver, rokso, saian, santipu_, scokaf, slvDev, tsvetanovv, ubl4nk, ulqiorra, yamapyblack, zaskoh
235.2398 USDC - $235.24
getReward
lacks access controlgetRewards
must be executed by the citizen contract to update and claim rewards. But it lacks access control, and can be executed by anyone.
/** This function is called by the S1 Citizen contract to emit BYTES to callers based on their state from the staker contract. @param _to The reward address to mint BYTES to. */ function getReward ( address _to ) external
hasRight
will revert at expiration timestampIn PermitControl, a user is given permission for a role until an expiration time. But in function hasRight
, the condition permissions[_address][_circumstance][_right] > block.timestamp
does not include the expiration time. So the function will revert at expiration time.
function hasRight ( address _address, bytes32 _circumstance, bytes32 _right ) public view returns (bool) { return permissions[_address][_circumstance][_right] > block.timestamp; }
Change the condition to include the expiration time
permissions[_address][_circumstance][_right] >= block.timestamp
_expirationTime
is not validatedIn setPermit function, expiration time is not validated. So it can be set in the past
function setPermit ( address _address, bytes32 _circumstance, bytes32 _right, uint256 _expirationTime ) public virtual hasValidPermit(UNIVERSAL, managerRight[_right]) { require(_right != ZERO_RIGHT, "P2"); permissions[_address][_circumstance][_right] = _expirationTime; emit PermitUpdated( _msgSender(), _address, _circumstance, _right, _expirationTime ); }
function renounceOwnership() public virtual onlyOwner { _setOwner(address(0)); }
function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _setOwner(newOwner); }
Performing division before multiplication can cause intermediate results to truncate and will lead to smaller value
Consider performing multiplication before division
uint256 points = amount * 100 / 1e18 * timelockMultiplier / _DIVISOR;
uint256 share = points * _PRECISION / pool.totalPoints * totalReward;
In configuration functions array lengths are not validated. If array lengths are not validated, it may cause unexpected behaviour.
import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "../access/PermitControl.sol"; import "../interfaces/IByteContract.sol"; import "../interfaces/IStaker.sol";
import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "../access/PermitControl.sol"; import "../interfaces/IByteContract.sol"; import "../interfaces/IGenericGetter.sol";
enum AssetType { S1_CITIZEN, S2_CITIZEN, BYTES, LP } function stake ( AssetType _assetType, uint256 _timelockId, uint256, uint256, uint256 ) external nonReentrant { // Validate that the asset being staked is of a valid type. if (uint8(_assetType) > 4) { revert InvalidAssetType(uint256(_assetType)); }
Use delete keyword instead of setting the variable to default values
stakedCitizen.stakedBytes = 0; stakedCitizen.timelockEndTime = 0; stakedCitizen.points = 0; stakedCitizen.hasVault = false; stakedCitizen.stakedVaultId = 0;
stakedCitizen.stakedBytes = 0; stakedCitizen.timelockEndTime = 0; stakedCitizen.points = 0;
#0 - c4-judge
2023-03-17T02:40:10Z
hansfriese marked the issue as grade-a
#1 - TimTinkers
2023-03-21T02:14:27Z
Thanks for taking a look at PermitControl
, though that was out of scope. We will implement some of these.
#2 - c4-sponsor
2023-03-21T02:14:31Z
TimTinkers marked the issue as sponsor acknowledged