Platform: Code4rena
Start Date: 21/12/2023
Pot Size: $90,500 USDC
Total HM: 10
Participants: 39
Period: 18 days
Judge: LSDan
Total Solo HM: 5
Id: 315
League: ETH
Rank: 11/39
Findings: 3
Award: $438.30
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: IllIllI
Also found by: 0x11singh99, 0xA5DF, 0xMilenov, 0xTheC0der, 7ashraf, Bauchibred, EV_om, Kaysoft, Sathish9098, SpicyMeatball, cheatc0d3, erebus, hash, imare, immeas, joaovwfreire, lil_eth, lsaudit, oakcobalt, para8956, peanuts, rvierdiiev, slvDev, trachev
21.8971 USDC - $21.90
On leap years, the number of days is 366, so calculations during those years will return the wrong value
FILE: 2023-12-autonolas/governance/contracts/OLAS.sol 21: // One year interval 22: uint256 public constant oneYear = 1 days * 365;
FILE: 2023-12-autonolas/tokenomics/contracts/TokenomicsConstants.sol // One year in seconds uint256 public constant ONE_YEAR = 1 days * 365;
Contract uses block.timestamp to determine when bonds mature.
Minor Manipulation Possible: Miners have some leeway to manipulate the block timestamp. They can't deviate wildly from the real time, but they can adjust the timestamp by small amounts (usually on the order of seconds to a few minutes).
Low Incentive for Abuse: In most cases, miners have little incentive to manipulate the timestamp in a way that would significantly impact your contract. The potential gain from slightly adjusting bond maturity times is generally low.
FILE: 2023-12-autonolas/tokenomics/contracts/Depository.sol 215: uint256 maturity = block.timestamp + vesting;
Since the contract executes the payload without internal validation, any vulnerabilities or flaws in the payload could be exploited, potentially compromising the multisig wallet.
If the payload is malformed or contains errors, the execution might fail, or worse, it could execute unintended modifications (like removing an existing owner instead of adding a new one).
FILE: 2023-12-autonolas/registries/contracts/multisigs /GnosisSafeSameAddressMultisig.sol function create( address[] memory owners, uint256 threshold, bytes memory data ) external returns (address multisig) { // Check for the correct data length uint256 dataLength = data.length; if (dataLength < DEFAULT_DATA_LENGTH) { revert IncorrectDataLength(DEFAULT_DATA_LENGTH, data.length); } // Read the proxy multisig address (20 bytes) and the multisig-related data assembly { multisig := mload(add(data, DEFAULT_DATA_LENGTH)) } // Check that the multisig address corresponds to the authorized multisig proxy bytecode hash bytes32 multisigProxyHash = keccak256(multisig.code); if (proxyHash != multisigProxyHash) { revert UnauthorizedMultisig(multisig); } // If provided, read the payload that is going to change the multisig ownership and threshold // The payload is expected to be the `execTransaction()` function call with all its arguments and signature(s) if (dataLength > DEFAULT_DATA_LENGTH) { uint256 payloadLength = dataLength - DEFAULT_DATA_LENGTH; bytes memory payload = new bytes(payloadLength); for (uint256 i = 0; i < payloadLength; ++i) { payload[i] = data[i + DEFAULT_DATA_LENGTH]; } // Call the multisig with the provided payload (bool success, ) = multisig.call(payload); if (!success) { revert MultisigExecFailed(multisig); } } // Get the provided proxy multisig owners and threshold address[] memory checkOwners = IGnosisSafe(multisig).getOwners(); uint256 checkThreshold = IGnosisSafe(multisig).getThreshold(); // Verify updated multisig proxy for provided owners and threshold if (threshold != checkThreshold) { revert WrongThreshold(checkThreshold, threshold); } uint256 numOwners = owners.length; if (numOwners != checkOwners.length) { revert WrongNumOwners(checkOwners.length, numOwners); } // The owners' addresses in the multisig itself are stored in reverse order compared to how they were added: // https://etherscan.io/address/0xd9db270c1b5e3bd161e8c8503c55ceabee709552#code#F6#L56 // Thus, the check must be carried out accordingly. for (uint256 i = 0; i < numOwners; ++i) { if (owners[i] != checkOwners[numOwners - i - 1]) { revert WrongOwner(owners[i]); } } }
Dry-Run Simulation: Before executing the payload on the actual Gnosis Safe contract, run a simulation or a "dry-run" of the payload in a test environment or a separate function to predict its effects.
The GnosisSafeSameAddressMultisig contract's correctness in verifying owners depends entirely on this specific storage order of Gnosis Safe.
If Gnosis Safe ever changes this detail (e.g., owners are stored in the order they were added), the verification logic in GnosisSafeSameAddressMultisig would fail to correctly verify the owners.
This reliance on an external contract's specific implementation detail, albeit stable for now, introduces a point of fragility in the GnosisSafeSameAddressMultisig contract.
Initial Owners
: Imagine a Gnosis Safe with two owners, Alice and Bob.
Adding a New Owner
: A new owner, Charlie, is added, making the owner list Alice, Bob, Charlie.
Gnosis Safe Storage
: Internally, Gnosis Safe stores them as Charlie, Bob, Alice.
Verification in GnosisSafeSameAddressMultisig
: If GnosisSafeSameAddressMultisig is verifying the owners, it should expect Charlie, Bob, Alice, not Alice, Bob, Charlie.
FILE: 2023-12-autonolas/tokenomics/contracts /Depository.sol function create( address[] memory owners, uint256 threshold, bytes memory data ) external returns (address multisig) { // Check for the correct data length uint256 dataLength = data.length; if (dataLength < DEFAULT_DATA_LENGTH) { revert IncorrectDataLength(DEFAULT_DATA_LENGTH, data.length); } // Read the proxy multisig address (20 bytes) and the multisig-related data assembly { multisig := mload(add(data, DEFAULT_DATA_LENGTH)) } // Check that the multisig address corresponds to the authorized multisig proxy bytecode hash bytes32 multisigProxyHash = keccak256(multisig.code); if (proxyHash != multisigProxyHash) { revert UnauthorizedMultisig(multisig); } // If provided, read the payload that is going to change the multisig ownership and threshold // The payload is expected to be the `execTransaction()` function call with all its arguments and signature(s) if (dataLength > DEFAULT_DATA_LENGTH) { uint256 payloadLength = dataLength - DEFAULT_DATA_LENGTH; bytes memory payload = new bytes(payloadLength); for (uint256 i = 0; i < payloadLength; ++i) { payload[i] = data[i + DEFAULT_DATA_LENGTH]; } // Call the multisig with the provided payload (bool success, ) = multisig.call(payload); if (!success) { revert MultisigExecFailed(multisig); } } // Get the provided proxy multisig owners and threshold address[] memory checkOwners = IGnosisSafe(multisig).getOwners(); uint256 checkThreshold = IGnosisSafe(multisig).getThreshold(); // Verify updated multisig proxy for provided owners and threshold if (threshold != checkThreshold) { revert WrongThreshold(checkThreshold, threshold); } uint256 numOwners = owners.length; if (numOwners != checkOwners.length) { revert WrongNumOwners(checkOwners.length, numOwners); } // The owners' addresses in the multisig itself are stored in reverse order compared to how they were added: // https://etherscan.io/address/0xd9db270c1b5e3bd161e8c8503c55ceabee709552#code#F6#L56 // Thus, the check must be carried out accordingly. for (uint256 i = 0; i < numOwners; ++i) { if (owners[i] != checkOwners[numOwners - i - 1]) { revert WrongOwner(owners[i]); } } }
Implement a mechanism to verify the version or specific implementation of the Gnosis Safe contract being interacted with, ensuring it matches the expected logic.
The absence of events does not directly impact the contract’s core functionality. The contract operations (like creating a multisig) will execute as intended, regardless of whether events are emitted.
The primary impact is on transparency and traceability. For users or developers trying to track the contract's activities, the absence of events makes it more difficult to see the history and results of transactions, especially in a decentralize.
FILE: 2023-12-autonolas/registries/contracts/multisigs /GnosisSafeMultisig.sol /// @dev Creates a gnosis safe multisig. /// @param owners Set of multisig owners. /// @param threshold Number of required confirmations for a multisig transaction. /// @param data Packed data related to the creation of a chosen multisig. /// @return multisig Address of a created multisig. function create( address[] memory owners, uint256 threshold, bytes memory data ) external returns (address multisig) { // Parse the data into gnosis-specific set of variables (address to, address fallbackHandler, address paymentToken, address payable paymentReceiver, uint256 payment, uint256 nonce, bytes memory payload) = _parseData(data); // Encode the gnosis setup function parameters bytes memory safeParams = abi.encodeWithSelector(GNOSIS_SAFE_SETUP_SELECTOR, owners, threshold, to, payload, fallbackHandler, paymentToken, payment, paymentReceiver); // Create a gnosis safe multisig via the proxy factory multisig = IGnosisSafeProxyFactory(gnosisSafeProxyFactory).createProxyWithNonce(gnosisSafe, safeParams, nonce); }
Add event
event MultisigCreated(address indexed multisig, address[] owners, uint256 threshold);
Function: checkpoint()
FILE: 2023-12-autonolas/tokenomics/contracts /Tokenomics.sol // Bonding and top-ups in OLAS are recalculated based on the inflation schedule per epoch // Actual maxBond of the epoch tp.epochPoint.totalTopUpsOLAS = uint96(inflationPerEpoch); incentives[4] = (inflationPerEpoch * tp.epochPoint.maxBondFraction) / 100;
The calculation of inflationPerEpoch multiplies curInflationPerSecond (a uint96 value) by the difference in seconds (diffNumSeconds), which is likely a large number. The result is then used in a percentage-based calculation for incentives[4]. The division by 100 can lead to precision loss since Solidity does not support floating-point arithmetic.
#0 - c4-pre-sort
2024-01-10T14:44:55Z
alex-ppg marked the issue as insufficient quality report
#1 - alex-ppg
2024-01-10T14:51:11Z
L-1 dupe of #430
#2 - c4-judge
2024-01-18T21:52:14Z
dmvt marked the issue as grade-b
🌟 Selected for report: 0xAnah
Also found by: 0x11singh99, 0xA5DF, IllIllI, JCK, Raihan, Sathish9098, alphacipher, c3phas, hunter_w3b, naman1778
52.4645 USDC - $52.46
manager
and _locked
state variables can be packed within same slot : Saves 2000 GAS
, 1 SLOT
_locked can be a uint8 instead of a uint256. It only stores the values 1 and 2 because the _locked variable is used solely for the Reentrancy lock. Therefore, using uint8 is perfectly safe and will not affect the protocol.
FILE: 2023-12-autonolas/registries/contracts/GenericRegistry.sol // Owner address address public owner; // Unit manager address public manager; + // Reentrancy lock + uint8 internal _locked = 1; // Base URI string public baseURI; // Unit counter uint256 public totalSupply; // Reentrancy lock - uint256 internal _locked = 1;
The check IVotingEscrow(ve).getVotes(donator) >= veOLASThreshold might be redundant across all iterations of your loop. If the donator's vote count and the veOLASThreshold remain constant throughout the execution of this loop (which they likely do), the result of IVotingEscrow(ve).getVotes(donator) >= veOLASThreshold will be the same for each iteration.
By doing this, you reduce the number of calls to IVotingEscrow(ve).getVotes(donator), which can save gas, especially if this is a call to an external contract. Saves 2100 GAS
for every iterations and external call
FILE: 2023-12-autonolas/tokenomics/contracts/Tokenomics.sol // Loop over service Ids to calculate their partial contributions + bool isDonatorEligible = IVotingEscrow(ve).getVotes(donator) >= veOLASThreshold) ? true : false; for (uint256 i = 0; i < numServices; ++i) { // Check if the service owner or donator stakes enough OLAS for its components / agents to get a top-up // If both component and agent owner top-up fractions are zero, there is no need to call external contract // functions to check each service owner veOLAS balance bool topUpEligible; if (incentiveFlags[2] || incentiveFlags[3]) { address serviceOwner = IToken(serviceRegistry).ownerOf(serviceIds[i]); topUpEligible = (IVotingEscrow(ve).getVotes(serviceOwner) >= veOLASThreshold || - IVotingEscrow(ve).getVotes(donator) >= veOLASThreshold) ? true : false; + isDonatorEligible ; }
nextEpochLen
, nextVeOLASThreshold
storage variables should be cachedFILE: 2023-12-autonolas/tokenomics/contracts/Tokenomics.sol // Update epoch length and set the next value back to zero + uint32 nextEpochLen_ = nextEpochLen ; - if (nextEpochLen > 0) { + if (nextEpochLen_ > 0) { - curEpochLen = nextEpochLen; - epochLen = uint32(curEpochLen); + epochLen = uint32(nextEpochLen_); nextEpochLen = 0; } // Update veOLAS threshold and set the next value back to zero + uint96 nextVeOLASThreshold_ = nextVeOLASThreshold ; - if (nextVeOLASThreshold > 0) { + if (nextVeOLASThreshold_ > 0) { - veOLASThreshold = nextVeOLASThreshold; + veOLASThreshold = nextVeOLASThreshold_; nextVeOLASThreshold = 0; }
lastPointNumber - 1
subtraction can be uncheckedUsing unchecked for lastPointNumber - 1 is a good optimization to reduce gas costs. Since already checked that lastPointNumber > 0, the subtraction will not underflow.
FILE: Breadcrumbs2023-12-autonolas/governance/contracts/veOLAS.sol uint256 lastPointNumber = mapUserPoints[account].length; if (lastPointNumber > 0) { + unchecked { pv = mapUserPoints[account][lastPointNumber - 1]; + } }
FILE: 2023-12-autonolas/governance/contracts/multisigs/GuardCM.sol function _verifyData(address target, bytes memory data, uint256 chainId) internal { // Push a pair of key defining variables into one key // target occupies first 160 bits uint256 targetSelectorChainId = uint256(uint160(target)); // selector occupies next 32 bits targetSelectorChainId |= uint256(uint32(bytes4(data))) << 160; // chainId occupies next 64 bits targetSelectorChainId |= chainId << 192; // Check the authorized combination of target and selector if (!mapAllowedTargetSelectorChainIds[targetSelectorChainId]) { revert NotAuthorized(target, bytes4(data), chainId); } }
function _verifyData(address target, bytes memory data, uint256 chainId) internal { require(data.length >= 4, "Data too short"); // Combine target, selector, and chainId into one key using bit manipulation uint256 targetSelectorChainId = (uint256(uint160(target)) | (uint256(bytes4(data)) << 160) | (chainId << 192)); // Check the authorized combination if (!mapAllowedTargetSelectorChainIds[targetSelectorChainId]) { revert NotAuthorized(target, bytes4(data), chainId); } }
Combined Bitwise Operations
:
The original code performs separate bitwise operations and assignments to compute targetSelectorChainId. In the optimized version, these operations are combined into a single expression. This reduces the number of assignment operations and makes the code more concise.
Data Length Check: Added a require statement to ensure that the data array is at least 4 bytes long. This is necessary because the code assumes data contains at least 4 bytes (to extract bytes4(data)). Without this check, the code could potentially revert due to an out-of-bounds access.
We can cache the repeated computation of _timeLaunch + ONE_YEAR
to avoid calculating it multiple times.
FILE: 2023-12-autonolas/tokenomics/contracts/Tokenomics.sol // Time launch of the OLAS contract uint256 _timeLaunch = IOLAS(_olas).timeLaunch(); // Check that the tokenomics contract is initialized no later than one year after the OLAS token is deployed + uint timeLaunchPlusOneYear = _timeLaunch + ONE_YEAR ; - if (block.timestamp >= (_timeLaunch + ONE_YEAR)) { + if (block.timestamp >= timeLaunchPlusOneYear ) { - revert Overflow(_timeLaunch + ONE_YEAR, block.timestamp); + revert Overflow(timeLaunchPlusOneYear , block.timestamp); } // Seconds left in the deployment year for the zero year inflation schedule // This value is necessary since it is different from a precise one year time, as the OLAS contract started earlier - uint256 zeroYearSecondsLeft = uint32(_timeLaunch + ONE_YEAR - block.timestamp); + uint256 zeroYearSecondsLeft = uint32(timeLaunchPlusOneYear - block.timestamp);
eBond -= amount
subtraction can be uncheckedIf certain that eBond is always greater than or equal to amount, using unchecked for the subtraction eBond -= amount can save some gas by skipping overflow/underflow checks.
FILE: 2023-12-autonolas/tokenomics/contracts/Tokenomics.sol // Effective bond must be bigger than the requested amount uint256 eBond = effectiveBond; if (eBond >= amount) { + unchecked { // The effective bond value is adjusted with the amount that is reserved for bonding // The unrealized part of the bonding amount will be returned when the bonding program is closed eBond -= amount; + } effectiveBond = uint96(eBond); success = true; emit EffectiveBondUpdated(eBond); }
uint32(_epochLen) is used to convert _epochLen to a 32-bit unsigned integer. This is done three times within this snippet.
FILE: 2023-12-autonolas/tokenomics/contracts/Tokenomics.sol + uint32 tempEpochLen = uint32(_epochLen); // Caching the typecast result // Check for the epochLen value to change - if (uint32(_epochLen) >= MIN_EPOCH_LENGTH && uint32(_epochLen) <= ONE_YEAR) { + if (tempEpochLen >= MIN_EPOCH_LENGTH && tempEpochLen <= ONE_YEAR) { nextEpochLen = tempEpochLen; } else { _epochLen = epochLen; }
FILE: Treasury.sol // Increase the amount of LP token reserves - uint256 reserves = mapTokenReserves[token] + tokenAmount; - mapTokenReserves[token] = reserves; + mapTokenReserves[token] = mapTokenReserves[token] + tokenAmount;
owner
storage variable should be cached with stack variableCaching the owner in a state variable is more gas-efficient compared to making recursive calls. This approach saves 300 gas and 3 SLOAD
FILE: 2023-12-autonolas/tokenomics/contracts/Depository.sol 123: function changeOwner(address newOwner) external { // Check for the contract ownership + address owner_ = owner ; - if (msg.sender != owner) { + if (msg.sender != owner_) { - revert OwnerOnly(msg.sender, owner); + revert OwnerOnly(msg.sender, owner_); } // Check for the zero address if (newOwner == address(0)) { revert ZeroAddress(); } owner = newOwner; emit OwnerUpdated(newOwner); } 143: function changeManagers(address _tokenomics, address _treasury) external { // Check for the contract ownership + address owner_ = owner ; - if (msg.sender != owner) { + if (msg.sender != owner_) { - revert OwnerOnly(msg.sender, owner); + revert OwnerOnly(msg.sender, owner_); } 163: function changeBondCalculator(address _bondCalculator) external { // Check for the contract ownership + address owner_ = owner ; - if (msg.sender != owner) { + if (msg.sender != owner_) { - revert OwnerOnly(msg.sender, owner); + revert OwnerOnly(msg.sender, owner_); } 183: function create(address token, uint256 priceLP, uint256 supply, uint256 vesting) external returns (uint256 productId) { // Check for the contract ownership + address owner_ = owner ; - if (msg.sender != owner) { + if (msg.sender != owner_) { - revert OwnerOnly(msg.sender, owner); + revert OwnerOnly(msg.sender, owner_); } 244: function close(uint256[] memory productIds) external returns (uint256[] memory closedProductIds) { // Check for the contract ownership + address owner_ = owner ; - if (msg.sender != owner) { + if (msg.sender != owner_) { - revert OwnerOnly(msg.sender, owner); + revert OwnerOnly(msg.sender, owner_); }
tokenomics
storage variable should be cached with stack variableCaching the tokenomics in a state variable is more gas-efficient compared to making recursive calls. This approach saves 100 gas and 1 SLOAD
FILE: 2023-12-autonolas/tokenomics/contracts/Depository.sol 225: // Check if the bond amount is beyond the limits + address tokenomics_ = tokenomics ; - if (!ITokenomics(tokenomics).reserveAmountForBondProgram(supply)) { + if (!ITokenomics(tokenomics_).reserveAmountForBondProgram(supply)) { - revert LowerThan(ITokenomics(tokenomics).effectiveBond(), supply); + revert LowerThan(ITokenomics(tokenomics_).effectiveBond(), supply); }
epsilonRate
and timeLaunch
storage variable should be cached with stack variableCaching the epsilonRate in a state variable is more gas-efficient compared to making recursive calls. This approach saves 100 gas and 1 SLOAD
FILE: 2023-12-autonolas/tokenomics/contracts/Tokenomics.sol + uint64 epsilonRate_ = epsilonRate ; // Compare with epsilon rate and choose the smallest one - if (fKD > epsilonRate) { + if (fKD > epsilonRate_) { - fKD = epsilonRate; + fKD = epsilonRate_; } 924: // Current year + uint32 timeLaunch_ = timeLaunch ; - uint256 numYears = (block.timestamp - timeLaunch) / ONE_YEAR; + uint256 numYears = (block.timestamp - timeLaunch_) / ONE_YEAR; // Amounts for the yearly inflation change from year to year, so if the year changes in the middle // of the epoch, it is necessary to adjust epoch inflation numbers to account for the year change if (numYears > currentYear) { // Calculate remainder of inflation for the passing year // End of the year timestamp - uint256 yearEndTime = timeLaunch + numYears * ONE_YEAR; + uint256 yearEndTime = timeLaunch_ + numYears * ONE_YEAR;
FILE: 2023-12-autonolas/governance/contracts/veOLAS.sol function getUserPoint(address account, uint256 idx) public view returns (PointVoting memory uPoint) { // Get the number of user points - uint256 userNumPoints = IVEOLAS(ve).getNumUserPoints(account); - if (userNumPoints > 0) { + if (IVEOLAS(ve).getNumUserPoints(account) > 0) { uPoint = IVEOLAS(ve).getUserPoint(account, idx); } }
Using inline assembly in Solidity for back-to-back external calls can indeed optimize gas usage, particularly by reducing the overhead associated with these calls.
FILE: 2023-12-autonolas/governance/contracts/wveOLAS.sol // Get the total number of supply points uint256 numPoints = IVEOLAS(ve).totalNumPoints(); PointVoting memory sPoint = IVEOLAS(ve).mapSupplyPoints(numPoints);
getSupplyCapForYear()
function could be more gas efficientImplemented an efficient algorithm for compound interest calculation that uses fewer iterations, similar to exponentiation by squaring. This reduces the number of loop iterations significantly for large numYears.
FILE: Breadcrumbs2023-12-autonolas/tokenomics/contracts /TokenomicsConstants.sol function getSupplyCapForYear(uint256 numYears) public pure returns (uint256 supplyCap) { // For the first 10 years the supply caps are pre-defined if (numYears < 10) { uint96[10] memory supplyCaps = [ 529_659_000_00e16, 569_913_084_00e16, 641_152_219_50e16, 708_500_141_72e16, 771_039_876_00e16, 828_233_282_97e16, 879_860_040_11e16, 925_948_139_65e16, 966_706_331_40e16, 1_000_000_000e18 ]; supplyCap = supplyCaps[numYears]; } else { // Number of years after ten years have passed (including ongoing ones) numYears -= 9; // Max cap for the first 10 years supplyCap = 1_000_000_000e18; // After that the inflation is 2% per year as defined by the OLAS contract uint256 maxMintCapFraction = 2; // Get the supply cap until the current year for (uint256 i = 0; i < numYears; ++i) { supplyCap += (supplyCap * maxMintCapFraction) / 100; } // Return the difference between last two caps (inflation for the current year) return supplyCap; } }
function getSupplyCapForYear(uint256 numYears) public pure returns (uint256 supplyCap) { uint96[10] memory supplyCaps = [ 529_659_000_00e16, 569_913_084_00e16, 641_152_219_50e16, 708_500_141_72e16, 771_039_876_00e16, 828_233_282_97e16, 879_860_040_11e16, 925_948_139_65e16, 966_706_331_40e16, 1_000_000_000e18 ]; if (numYears < 10) { return supplyCaps[numYears]; } // Calculate compounded inflation using a more efficient formula return calculateCompoundedInflation(1_000_000_000e18, numYears - 9, 2); } function calculateCompoundedInflation(uint256 initialCap, uint256 years, uint256 inflationRate) internal pure returns (uint256) { while (years > 0) { if (years % 2 == 1) { initialCap += (initialCap * inflationRate) / 100; } inflationRate = (inflationRate * 2) % 100; years /= 2; } return initialCap; }
The original method calculates compounded inflation by iteratively adding 2% of the current supply cap to the cap for each year beyond the 10th. In this linear approach, the number of iterations (and thus operations) is directly proportional to the number of years. For example, for 20 years beyond the 10th, it performs 20 iterations.
The optimized method changes the way the compounded inflation is calculated. Instead of linearly iterating through each year, it uses a method that can be likened to exponentiation by squaring, a technique to efficiently compute powers. In this method, the number of operations grows logarithmically with the number of years, not linearly. This is because each iteration effectively squares the effect of the inflation, drastically reducing the number of iterations required as the number of years increases.
Consider calculating the supply cap for 20 years after the 10th year. Linear Approach: Requires 20 iterations. Optimized Approach: It effectively halves the number of remaining years in each iteration. The number of iterations is around logâ‚‚(20), which is about 4 or 5 iterations, significantly less than 20.
getCurrentPriceLP
for better gas efficiencyThe getCurrentPriceLP function, we can refine the optimizations further to enhance gas efficiency. The primary goal is to streamline the code by eliminating redundant operations and ensuring the calculations are done in the most gas-efficient manner.
FILE: 2023-12-autonolas/tokenomics/contracts/GenericBondCalculator.sol function getCurrentPriceLP(address token) external view returns (uint256 priceLP) { IUniswapV2Pair pair = IUniswapV2Pair(token); uint256 totalSupply = pair.totalSupply(); if (totalSupply > 0) { address token0 = pair.token0(); address token1 = pair.token1(); uint256 reserve0; uint256 reserve1; // requires low gas (reserve0, reserve1, ) = pair.getReserves(); // token0 != olas && token1 != olas, this should never happen if (token0 == olas || token1 == olas) { // If OLAS is in token0, assign its reserve to reserve1, otherwise the reserve1 is already correct if (token0 == olas) { reserve1 = reserve0; } // Calculate the LP price based on reserves and totalSupply ratio multiplied by 1e18 // Inspired by: https://github.com/curvefi/curve-contract/blob/master/contracts/pool-templates/base/SwapTemplateBase.vy#L262 priceLP = (reserve1 * 1e18) / totalSupply; } } }
function getCurrentPriceLP(address token) external view returns (uint256 priceLP) { IUniswapV2Pair pair = IUniswapV2Pair(token); (uint256 reserve0, uint256 reserve1, ) = pair.getReserves(); if (reserve0 == 0 || reserve1 == 0) { return 0; } uint256 totalSupply = pair.totalSupply(); if (totalSupply == 0) { return 0; } address token0 = pair.token0(); priceLP = (token0 == olas ? reserve0 : reserve1) * 1e18 / totalSupply; }
Early Check for Zero Reserves
: Check if either reserve0 or reserve1 is zero at the beginning. If either is zero, the function returns early. This is based on the assumption that if either reserve is zero, the price calculation isn't meaningful.Gas Saving: This check prevents further execution if there is no liquidity in the pool, saving gas in such scenarios.
Deferred Total Supply Check
: The totalSupply check is moved after the reserves check. Since the total supply is irrelevant if the reserves are zero, this ordering is more efficient.Gas Saving: This ensures that the (potentially more expensive) totalSupply call is only made if necessary, saving gas when there are no reserves.
Streamlined Price Calculation
: The price calculation is condensed into a single line. This is more concise and avoids the need for a conditional statement to swap reserve0 and reserve1.Gas Saving: Reducing the number of conditional statements and assignments decreases the computational steps and thus the gas cost.
Remove Redundant Token Check
: The function no longer checks for the case where neither token0 nor token1 is olas, as it's assumed that olas will be one of the tokens in the pair. This removes an unnecessary condition.Gas Saving: Fewer conditional checks mean fewer operations to perform, leading to lower gas usage.
!matured
this check is redundant inside the for loopParticularly addressing the redundant check of the matured flag. Since matured does not change within the loop, we can optimize the function by checking its value outside the loop and altering the loop's behavior accordingly.
FILE: 2023-12-autonolas/tokenomics/contracts/Depository.sol function getBonds(address account, bool matured) external view returns (uint256[] memory bondIds, uint256 payout) { // Check the address if (account == address(0)) { revert ZeroAddress(); } uint256 numAccountBonds; // Calculate the number of pending bonds uint256 numBonds = bondCounter; bool[] memory positions = new bool[](numBonds); // Record the bond number if it belongs to the account address and was not yet redeemed for (uint256 i = 0; i < numBonds; ++i) { // Check if the bond belongs to the account // If not and the address is zero, the bond was redeemed or never existed if (mapUserBonds[i].account == account) { // Check if requested bond is not matured but owned by the account address if (!matured || // Or if the requested bond is matured, i.e., the bond maturity timestamp passed block.timestamp >= mapUserBonds[i].maturity) { positions[i] = true; ++numAccountBonds; // The payout is always bigger than zero if the bond exists payout += mapUserBonds[i].payout; } } }
#0 - c4-pre-sort
2024-01-10T16:03:35Z
alex-ppg marked the issue as sufficient quality report
#1 - c4-judge
2024-01-18T21:31:26Z
dmvt marked the issue as grade-b
#2 - sathishpic22
2024-01-23T16:30:44Z
Hi @dmvt
Thank you for your prompt evaluation.
I would like to express my concern regarding the assessment of my reports. I am confident in the validity and high quality of my findings, which, as demonstrated, contribute significantly to gas savings. Additionally, my reports consistently highlight low-impact findings more extensively than others.
However, I have noticed that reports receiving an 'A' grade seem to encompass fewer impactful findings and often overlook instances from bot races. It appears that there is a tendency to disregard certain findings in these evaluations.
I firmly believe that a more thorough review of my submissions will reveal their substantial value and alignment with our objectives. I appreciate your attention to this matter and look forward to any feedback that could further enhance the quality of my work.
[G-1] manager and _locked state variables can be packed within same slot : Saves 2000 GAS , 1 SLOT
[G-2] IVotingEscrow(ve).getVotes(donator) >= veOLASThreshold check is redundant
This finding will saves 1 external call for even iterations and saves 2100 GAS for each iterations.
[G-3] nextEpochLen , nextVeOLASThreshold storage variables should be cached - Saves 400 GAS
and 4 SLOD
[G-4] lastPointNumber - 1 subtraction can be unchecked - Saves 100-200 GAS
After unchecked
[G-5] Optimize the _verifyData() function for better gas efficiency
[G-6] Cache _timeLaunch + ONE_YEAR computation to optimize gas - Saves 2 redundant computations and saves 1000 GAS
[G-7] eBond -= amount subtraction can be unchecked
[G-8] Caching the Result of Typecasting uint32(_epochLen) to avoid repetitive typecasting
[G-9] Don't Cache state variables only used once
[G-10] owner storage variable should be cached with stack variable - Saves 500 GAS
, 5 SLOD
[G-11] tokenomics storage variable should be cached with stack variable
[G-12] epsilonRate and timeLaunch storage variable should be cached with stack variable
[G-13] Don't cache external calls only used once
[G-14] Use assembly for back to back external calls
[G-15] Use efficient Compounded Inflation Calculations in getSupplyCapForYear() function could be more gas efficient
[G-16] Optimize getCurrentPriceLP for better gas efficiency
[G-17] !matured this check is redundant inside the for loop
I respectfully request a re-evaluation of my reports. Based on the depth and quality of my findings, I am confident that they merit a higher grade than currently assigned.
I appreciate the opportunity to present my perspective on this matter and am eager to engage in further discussions to clarify any aspects of my work. Your consideration of this request is greatly valued.
Thank you
#3 - dmvt
2024-01-23T20:32:58Z
While I appreciate your effort, gas reports are graded on a curve. I start by disregarding any issue that doesn't state how much gas will be saved. Without this metric, it takes more trouble than it's worth for the sponsor to evaluate. Sometimes this can be overridden by extremely detailed descriptions, but very infrequently. Your report had 6 valid issue under this metric. On the curve of all valid reports, this makes yours a B in this contest.
🌟 Selected for report: hunter_w3b
Also found by: Sathish9098, joaovwfreire, pavankv, peanuts
363.9423 USDC - $363.94
Olas functions as an integrated platform designed to facilitate a range of off-chain services, encompassing automation processes, oracle systems, and collaboratively owned artificial intelligence capabilities. It provides a comprehensive suite for the development and deployment of these services, underpinned by a protocol that promotes the incentivization of both the creation and ongoing decentralized operation of these services. This infrastructure is engineered to support a co-owned, decentralized model, emphasizing collaborative development and management within its ecosystem.
FxPortal
: Used for communication between Ethereum and Polygon. Relies on the security and efficiency of Polygon's PoS bridge.Arbitrary Message Bridge (AMB)
: Facilitates communication between Ethereum and Gnosis Chain.Blockchain reorganizations (reorgs) can affect the finality of cross-chain messages. A reorg on the source chain after a message has been sent but before it's finalized on the destination chain could result in discrepancies or double executions.
Delayed Response in Dynamic Situations
: In a rapidly changing environment, such as in response to security threats, market movements, or urgent protocol upgrades, the delay can hinder the protocol's ability to implement timely decisions.Reduced Flexibility
: The set delay period (which might be several days or even longer) means that once a proposal is approved, the protocol must wait for this period to elapse before any action can be taken, even if the situation demands an immediate response.Potential for Market Manipulation
: In cases where governance decisions are market-sensitive (e.g., tokenomic changes), the known delay period can be exploited by market participants for profit, potentially leading to price manipulation or front-running.Security vs. Agility Trade-off
: The protocol must balance the need for security (preventing hasty or malicious actions) with the need for agility (responding swiftly to critical issues).Unrepresentative Decisions
: With low participation, the decisions made could disproportionately reflect the will of a small group of voters, which might not align with the broader community's interest.Easier Manipulation
: Low turnout reduces the number of votes required to pass proposals, making it easier for a minority or even malicious actors to influence governance decisions.Reduced Protocol Legitimacy
: Consistently low participation can undermine the legitimacy of the governance process, as decisions are made by a small segment of the holder base.Complexity and Engagement
: The technical complexity of proposals or a disconnect between stakeholders' understanding and the matters at hand can lead to lower engagement.Token Distribution
: If veOLAS tokens are concentrated in the hands of a few holders, it can demotivate broader community participation, leading to apathy among smaller holders.Incentive Structures
: Lack of adequate incentives for voting can result in voter apathy.Risk
: The registries depend on each other (e.g., AgentRegistry depending on ComponentRegistry). Issues in one registry could cascade to others, especially considering the dependency checks in contracts like AgentRegistry.Impact
: A failure or vulnerability in one registry could compromise the integrity of another, affecting the overall reliability of the system.Risk
: The registries handle critical data, such as IPFS hashes and dependencies. Inadequate validation of this data could lead to incorrect or malicious data being registered.Impact
: Incorrect data could lead to faulty operations or could be used to exploit other parts of the system, such as the deployment of compromised agents or components.Risk
: Large liquidity providers might become overly influential, controlling a significant portion of the OLAS tokens.Impact
: This could lead to market manipulation or centralization of token ownership, affecting the token's price and stability.Risk
: The long-term viability of the tokenomics model is crucial. Unsustainable incentive models could lead to resource drainage.Impact
: If the incentives become unsustainable, it could lead to a decrease in developer participation and liquidity provision, harming the protocol's growth.Risk
: Token concentration in the hands of a few might lead to governance issues, where major holders wield disproportionate influence.Impact
: This could skew decision-making processes and lead to decisions that favor a minority of stakeholders.Risk
: If the incentive mechanisms are not well-designed, there's a risk of developers gaming the system to earn rewards without contributing meaningfully.Impact
: Such exploitation could drain resources and detract from the protocol's goal of fostering genuinely useful code.Even with rigorous testing and audits, there's always a risk of undiscovered bugs in smart contracts, which could lead to malfunctions or exploits. This includes risks in both the governance contracts (like GovernorOLAS) and associated contracts (like FxGovernorTunnel, HomeMediator, BridgedERC20).
If any part of the governance system is upgradeable, there's a risk associated with the upgrade process itself, including potential mismatches or incompatibilities between different contract versions.
Certain governance mechanisms could be vulnerable to DoS attacks, where an attacker deliberately floods the system with transactions or proposals to halt or slow down the governance process.
Functions dependent on block timestamps might be vulnerable to minor manipulations by miners, affecting processes that rely on specific timing conditions.
Malicious actors could attempt to exploit the public nature of pending transactions in the mempool to influence governance decisions, such as by front-running vote-related transactions.
Integration risks in the context of the Autonolas protocol, particularly considering its multi-chain focus and interaction with various blockchain networks and external systems, involve a range of technical challenges.
Risk Details
: When integrating with contracts on different blockchains or systems (like Ethereum, Polygon, and Gnosis Chain), there's a risk of compatibility issues due to differing contract standards, gas models, or blockchain functionalities.Technical Specifics
: Differences in EVM compatibility, state size limits, or gas pricing mechanisms across chains can affect the behavior of integrated contracts.Risk Details
: Reliance on cross-chain bridges (like FxPortal for Polygon and AMB for Gnosis Chain) for governance actions introduces risks of communication failures or discrepancies in data transmission.Technical Specifics
: Bridge mechanisms may face downtime, data throughput limitations, or inconsistencies in data validation and verification processes.Risk Details
: High network congestion on integrated blockchains can lead to delayed transaction confirmations, affecting time-sensitive operations.Technical Specifics
: Network overload, especially on chains with limited throughput, can result in increased transaction fees and delayed block confirmations.Risk Details
: Integrating various blockchain functionalities into a user-friendly interface poses challenges, where mismatches or errors can lead to user mistakes or misinterpretations.Technical Specifics
: Complex interactions required for cross-chain functionalities might not be intuitively presented in user interfaces, increasing the risk of errors.Risk Description
: Dependence on external oracles for market data or other inputs could introduce inaccuracies.Potential Impacts
:
Malfunctioning or manipulated oracles could skew incentive mechanisms, affecting staking rewards, liquidity incentives, and other crucial operations.function changeGovernor(address newGovernor) external { function changeGovernorCheckProposalId(uint256 proposalId) external { function changeOwner(address newOwner) external { function mint(address account, uint256 amount) external { function burn(uint256 amount) external { function pause() external virtual { function unpause() external virtual { function changeManager(address newManager) external virtual { function setBaseURI(string memory bURI) external virtual { function changeManagers(address _tokenomics, address _treasury) external { function changeBondCalculator(address _bondCalculator) external { function create(address token, uint256 priceLP, uint256 supply, uint256 vesting) external returns (uint256 productId) { function close(uint256[] memory productIds) external returns (uint256[] memory closedProductIds) { function changeTokenomicsImplementation(address implementation) external { function changeRegistries(address _componentRegistry, address _agentRegistry, address _serviceRegistry) external { function changeDonatorBlacklist(address _donatorBlacklist) external {
Governance Manipulation
: Unauthorized changes to governance roles or rules could lead to detrimental protocol changes or malicious governance actions.Token Supply Disruption
: The ability to mint and burn tokens could be exploited to manipulate the token's supply, potentially destabilizing its value and the protocol's economy.Unauthorized Contract Upgrades
: Changing contract implementations or altering critical settings could introduce vulnerabilities or alter the protocol’s intended behavior.Operational Disruption
: Functions like pausing contracts can halt critical operations, directly impacting users and the protocol's functionality.function reserveAmountForBondProgram(uint256 amount) external returns (bool success) { function refundFromBondProgram(uint256 amount) external {
Financial Losses
: Unauthorized access to reserve or refund functions could lead to misappropriation of funds, affecting the protocol's financial stability.Bond Program Manipulation
: The integrity of bond programs could be compromised, affecting investor confidence and the protocol's reputation.function trackServiceDonations( address donator, uint256[] memory serviceIds, uint256[] memory amounts, uint256 donationETH ) external {
Misallocation of Funds
: If the tracking or allocation of donations is manipulated, it could lead to improper use of funds, affecting the protocol's financial integrity.Data Integrity Issues
: Inaccurate tracking of donations could disrupt the protocol’s financial reporting and decision-making processes.function accountOwnerIncentives(address account, uint256[] memory unitTypes, uint256[] memory unitIds) external
Incentive Manipulation
: Unauthorized changes to incentive distributions could unfairly advantage certain users, disrupting the protocol's reward mechanisms and user trust.Resource Drainage
: Malicious distribution of incentives could drain resources, impacting the protocol's sustainability.The governance architecture involves various contracts and mechanisms working together to ensure decentralized decision-making and management. Based on the information provided earlier about contracts like GovernorOLAS, FxGovernorTunnel, HomeMediator, and others, let's construct an outline of the likely governance architecture.
Architecture assessment of the business logic based on the architecture of the Autonolas protocol's registries system (comprising UnitRegistry, ComponentRegistry, AgentRegistry, and RegistriesManager), we'll consider key architectural aspects and their alignment with business objectives
Crucial component designed to oversee and possibly regulate actions taken by a community-owned multisig wallet (CM). This contract likely functions as a safeguard within the governance framework, ensuring that actions taken by the CM align with the broader intentions and rules of the decentralized autonomous organization (DAO).
The contracts demonstrate modularity (e.g., inheriting from OpenZeppelin contracts). This approach should be maintained or enhanced to ensure code reusability and easier updates.
Design contracts with scalability in mind. This includes considering layer 2 solutions or sidechains for more efficient processing, especially for cross-chain functionalities.
Incorporate mechanisms for community feedback and governance decisions into the software development lifecycle, reflecting the decentralized ethos of the protocol.
Given the critical role of registries in managing on-chain representations of components and agents, a security-first approach is paramount. This includes following best practices like check-effects-interactions pattern, secure handling of external calls, and gas optimizations.
Implement mechanisms for data recovery and emergency handling in case of critical failures. This could include pausing functionality in the event of a detected anomaly or breach.
High levels of coverage across all metrics, indicating thorough testing.
-------------------------|----------|----------|----------|----------|----------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Lines |
---|---|---|---|---|---|
contracts\ | 100 | 93.84 | 100 | 98.81 | |
GovernorOLAS.sol | 100 | 100 | 100 | 100 | |
OLAS.sol | 100 | 100 | 100 | 100 | |
Timelock.sol | 100 | 100 | 100 | 100 | |
veOLAS.sol | 100 | 92.37 | 100 | 98.4 | 249,253,283,286 |
wveOLAS.sol | 100 | 100 | 100 | 100 | |
contracts\bridges\ | 96 | 100 | 95 | 97.62 | |
BridgedERC20.sol | 100 | 100 | 100 | 100 | |
FxERC20ChildTunnel.sol | 100 | 100 | 100 | 100 | |
FxERC20RootTunnel.sol | 78.57 | 100 | 80 | 85 | 74,77,79 |
FxGovernorTunnel.sol | 100 | 100 | 100 | 100 | |
HomeMediator.sol | 100 | 100 | 100 | 100 | |
contracts\bridges\test\ | 100 | 100 | 100 | 100 | |
FxRootMock.sol | 100 | 100 | 100 | 100 | |
contracts\interfaces\ | 100 | 100 | 100 | 100 | |
IERC20.sol | 100 | 100 | 100 | 100 | |
IErrors.sol | 100 | 100 | 100 | 100 | |
contracts\multisigs\ | 100 | 100 | 100 | 100 | |
GuardCM.sol | 100 | 100 | 100 | 100 | |
contracts\test\ | 75 | 100 | 80 | 75 | |
BrokenERC20.sol | 75 | 100 | 80 | 75 | 31 |
------------------------- | ---------- | ---------- | ---------- | ---------- | ---------------- |
All files | 98.79 | 97.19 | 98.36 | 98.7 | |
------------------------- | ---------- | ---------- | ---------- | ---------- | ---------------- |
veOLAS.sol
: Although most metrics are at 100%, there's a minor gap in branch coverage (92.37%) and line coverage (98.4%). Uncovered lines include 249, 253, 283, 286, suggesting potential edge cases or conditional branches not tested.
veOLAS.sol
: Increase test cases to cover the branches and lines mentioned above, focusing on the conditional logic that might not be fully exercised in the current test suite.
------------------------------------|----------|----------|----------|----------|----------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Lines |
---|---|---|---|---|---|
contracts\ | 90.54 | 78.13 | 86.21 | 87.34 | |
AgentRegistry.sol | 100 | 87.5 | 75 | 86.67 | 62,72 |
ComponentRegistry.sol | 100 | 100 | 100 | 100 | |
GenericManager.sol | 57.14 | 50 | 66.67 | 57.14 | ... 27,28,31,32 |
GenericRegistry.sol | 69.23 | 35.71 | 71.43 | 60 | ... 6,98,99,100 |
RegistriesManager.sol | 100 | 100 | 100 | 100 | |
UnitRegistry.sol | 100 | 100 | 100 | 100 | |
contracts\interfaces\ | 100 | 100 | 100 | 100 | |
IErrorsRegistries.sol | 100 | 100 | 100 | 100 | |
IRegistry.sol | 100 | 100 | 100 | 100 | |
contracts\multisigs\ | 80.77 | 81.82 | 100 | 83.72 | |
GnosisSafeMultisig.sol | 100 | 83.33 | 100 | 100 | |
GnosisSafeSameAddressMultisig.sol | 72.22 | 81.25 | 100 | 75 | ... 118,119,120 |
------------------------------------ | ---------- | ---------- | ---------- | ---------- | ---------------- |
All files | 88 | 79.07 | 88.24 | 86.57 | |
------------------------------------ | ---------- | ---------- | ---------- | ---------- | ---------------- |
GenericManager.sol
and GenericRegistry.sol show significantly lower coverage across all metrics. Specifically, GenericRegistry.sol has only 69.23% statement coverage and 35.71% branch coverage.
Uncovered lines in GenericRegistry.sol
include 96, 98, 99, 100, and in GenericManager.sol
, they include 25, 26, 27, 28, 31, 32.
GenericManager.sol
& GenericRegistry.sol
: Develop additional test cases to improve coverage, particularly targeting the conditional logic and functions that are currently under-tested.
AgentRegistry.sol
: Address uncovered lines (62, 72) to enhance branch coverage.
-----------------------------|----------|----------|----------|----------|----------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Lines |
---|---|---|---|---|---|
contracts\ | 100 | 95.22 | 100 | 98.56 | |
Depository.sol | 100 | 80 | 100 | 93.53 | ... 312,385,440 |
Dispenser.sol | 100 | 100 | 100 | 100 | |
DonatorBlacklist.sol | 100 | 100 | 100 | 100 | |
GenericBondCalculator.sol | 100 | 71.43 | 100 | 90 | 32,60 |
Tokenomics.sol | 100 | 100 | 100 | 100 | |
TokenomicsConstants.sol | 100 | 100 | 100 | 100 | |
TokenomicsProxy.sol | 100 | 100 | 100 | 100 | |
Treasury.sol | 100 | 100 | 100 | 100 | |
contracts\interfaces\ | 100 | 100 | 100 | 100 | |
IDonatorBlacklist.sol | 100 | 100 | 100 | 100 | |
IErrorsTokenomics.sol | 100 | 100 | 100 | 100 | |
IGenericBondCalculator.sol | 100 | 100 | 100 | 100 | |
IOLAS.sol | 100 | 100 | 100 | 100 | |
IServiceRegistry.sol | 100 | 100 | 100 | 100 | |
IToken.sol | 100 | 100 | 100 | 100 | |
ITokenomics.sol | 100 | 100 | 100 | 100 | |
ITreasury.sol | 100 | 100 | 100 | 100 | |
IUniswapV2Pair.sol | 100 | 100 | 100 | 100 | |
IVotingEscrow.sol | 100 | 100 | 100 | 100 | |
----------------------------- | ---------- | ---------- | ---------- | ---------- | ---------------- |
All files | 100 | 95.22 | 100 | 98.56 | |
----------------------------- | ---------- | ---------- | ---------- | ---------- | ---------------- |
Depository.sol
shows lower branch coverage (80%) compared to other metrics. Uncovered lines are 312, 385, 440.
GenericBondCalculator.sol
has a lower branch coverage of 71.43% and line coverage of 90%, with uncovered lines 32 and 60.
Depository.sol
& GenericBondCalculator.sol
: Enhance test cases focusing on branches and lines not currently covered. Pay particular attention to complex conditional statements and scenarios that may not be adequately represented in the current test suite.
Consistent Test Standards
: Ensure uniformity in test coverage standards across all contracts to maintain code quality and reliability.Branch and Edge Case Testing
: Prioritize testing for branches and edge cases, especially in contracts with lower branch coverage.Continuous Integration of Tests
: Implement continuous integration processes to automatically run tests and report coverage, aiding in identifying coverage gaps promptly.Affected Code
: mint and burn functions.Weakness
: Both functions are only callable by the owner. This centralization poses a risk, as the security of the entire token supply hinges on a single account.function mint(address account, uint256 amount) external { if (msg.sender != owner) { revert OwnerOnly(msg.sender, owner); } _mint(account, amount); } function burn(uint256 amount) external { if (msg.sender != owner) { revert OwnerOnly(msg.sender, owner); } _burn(msg.sender, amount); }
Affected Code
: processMessageFromRoot and processMessageFromForeign.Weakness
: The processing functions unpack and execute transaction data from cross-chain messages. Inadequate validation of this data can lead to execution of harmful transactions.
Code Snippet (FxGovernorTunnel):function processMessageFromRoot(uint256 stateId, address rootMessageSender, bytes memory data) external override { // ... validation checks ... for (uint256 i = 0; i < dataLength;) { // ... data unpacking and transaction execution ... } }
Affected Code
: propose and _execute functions.Weakness
: The timelock delays the execution of governance decisions, which can be a limitation in urgent situations.function _execute( uint256 proposalId, address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash ) internal override(Governor, GovernorTimelockControl) { super._execute(proposalId, targets, values, calldatas, descriptionHash); }
Affected Code
: Public functions involved in governance decisions and token operations in all contracts.Weakness
: The visibility of transactions in the mempool before being confirmed can lead to front-running, especially in governance and token-related transactions.15 hours
#0 - c4-pre-sort
2024-01-10T12:24:46Z
alex-ppg marked the issue as sufficient quality report
#1 - c4-judge
2024-01-18T21:52:57Z
dmvt marked the issue as grade-b
#2 - sathishpic22
2024-01-23T16:54:22Z
Hi @dmvt
Thank you for your prompt evaluation.
I believe that my report offers a more comprehensive and high-quality analysis compared to others. It merits a higher grade than currently assigned, as it introduces fresh insights without reiterating information already documented. Additionally, I have adhered to the report quality standards as per the C4 suggestions.
Explained possible systemic risk more technical way from each sections Governance
, Registries
, Tokenomics
.
Explained possible technical risks as per docs and implementations
Analyzed possible Integration risks based on implementations
demonstrated the risks related to single ownership control
In This sections explained the architecture explanation's using the diagrams .
Explained possible SW considerations to improve the protocol quality
I respectfully request a re-evaluation of my reports. I am confident that they deserve a higher grade than what has been assigned currently. After reviewing the reports that received Grade A, I am convinced that my analysis is more detailed and thorough.
I appreciate this opportunity to express my thoughts on this matter. Additionally, I have included architecture explanation diagrams to further substantiate the depth of my analysis.
Thank you for considering my request and for your attention to these details.
#3 - dmvt
2024-01-23T20:42:44Z
I appreciate your attention to detail on this report, but I have to disagree with it's value. I view analysis to be an add on which augments the real value a sponsor is looking for, high and medium risk issues. You have not found any, indicating that your understanding of the protocol is not in depth enough to provide information the sponsor is not already aware of. Your charts, while a nice addition, describe a system the sponsor designed and fully understands. The same is true of much of your analysis, including the test coverage.
The report contains a mixture of simplistic descriptions, automated tooling output, simple charts, and best practices that could apply to any codebase regardless of quality. You got a B for the effort involved in collating the information and an obvious desire to provide something of value, but this is not an A level analysis.