Platform: Code4rena
Start Date: 10/05/2024
Pot Size: $300,500 USDC
Total HM: 4
Participants: 27
Period: 17 days
Judge: Picodes
Total Solo HM: 1
Id: 375
League: ETH
Rank: 13/27
Findings: 1
Award: $0.00
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Sathish9098
Also found by: Audinarey, Dup1337, K42, KupiaSec, LessDupes, Rhaydden, SpicyMeatball, Takarez, ZanyBonzy, bronze_pickaxe, carlitox477, dontonka, forgebyola, fyamf, guhu95, hihen, josephdara, ladboy233, slvDev, twcctop, xuwinnie, zanderbyte
0 USDC - $0.00
4naly3er
report.Issue Type | Count |
---|---|
Low Severity Issues | 24 |
Non-Critical Issues | 19 |
Total Issues | 43 |
Error.sol: Inconsistent Error Parameter Names
DataTooLarge
error is defined with dataLength
and maxDataLength
parameters, but when it is used in the SequencerInbox
contract, fullDataLen
and maxDataSize
are passed instead. This inconsistency can lead to confusion and make the code harder to understand and maintain.DataTooLarge
error definition to match the variable names used in the SequencerInbox
contract.error DataTooLarge(uint256 fullDataLen, uint256 maxDataSize);
SequencerInbox.sol: Unused Errors
ForceIncludeTimeTooSoon
errors are imported but never used in the contract.// Remove unused errors // error ForceIncludeTimeTooSoon();
ISequencerInbox.sol: Similar Parameter Names
addSequencerL2Batch
, addSequencerL2BatchFromBlobs
, addSequencerL2BatchFromBlobsDelayProof
, addSequencerL2BatchFromOriginDelayProof
, and addSequencerL2BatchDelayProof
functions have similar parameter names (prevMessageCount
and newMessageCount
), which could lead to confusion when implementing or using these functions.prevMessageCount
and updatedMessageCount
.DelayBuffer.sol: Integer Division in calcBuffer
Function
calcBuffer
function uses integer division, which may result in loss of precision.uint256 buffer = Math.max(minBuffer, dataSize.div(minSize));
AssertionStakingPool.sol: Unchecked Return Value of safeIncreaseAllowance
safeIncreaseAllowance
function from SafeERC20
is used without checking the return value.safeIncreaseAllowance
to ensure the operation was successful.require(token.safeIncreaseAllowance(to, amount), "Allowance increase failed");
RollupUserLogic.sol: Misleading Comment on initialize
Function
initialize
function has a comment stating it "shouldn't write to state during init," but it does write state by validating _stakeToken
.// @dev the user logic validates configuration and ensures consistency on parameters.
RollupProxy.sol: Unused ContractDependencies
Struct
ContractDependencies
struct is not imported as it should be in this contract.ContractDependencies
struct in the way below, for better practice.// Import ContractDependencies struct like this for better practice // import { ContractDependencies } from "...";
RollupCreator.sol: Unused receive
Function
receive
function is defined but not used in this contract.receive
function or using it where appropriate.// Remove unused function // receive() external payable {...}
RollupCore.sol: Unbounded Array Growth
_stakerList
array can grow unbounded, which may lead to high gas costs when looping through it. Consider using a more gas-efficient data structure if the list is expected to grow large._stakerList
and consider using a more gas-efficient data structure if needed.RollupCore.sol: Multiple Entries for the Same Staker in _stakerMap
_stakerMap
mapping uses the staker's address as the key, which may lead to multiple entries for the same staker if they stake from different addresses._stakerMap
mapping to avoid duplicate entries.mapping(address => uint256) private stakerMap;
RollupAdminLogic.sol: High Gas Costs in forceRefundStaker
Function
forceRefundStaker
function loops through the provided staker
array, which may lead to high gas costs if the array is large. Consider adding a maximum limit to the number of stakers that can be processed in a single transaction.forceRefundStaker
function to avoid high gas costs.function forceRefundStaker(address[] calldata stakers) external { uint256 maxBatchSize = 100; for (uint256 i = 0; i < stakers.length && i < maxBatchSize; i++) { // function logic } }
IRollupAdmin.sol: Missing Description for Event Parameter
OwnerFunctionCalled
event is missing a description of the id
parameter, which may make it harder to understand its purpose.id
parameter in the OwnerFunctionCalled
event.event OwnerFunctionCalled(uint256 id); // id represents the function identifier
Config.sol: Mixed Immutable and Mutable Parameters
Config
struct contains a mix of immutable and mutable parameters, which may be confusing.Config
struct into separate structs for immutable and mutable parameters or add comments to clarify their mutability.struct ImmutableConfig { uint256 param1; } struct MutableConfig { uint256 param2; }
RollupUserLogic.sol: Lack of Error Handling for safeTransfer
Function
receiveTokens
function uses safeTransferFrom
without explicitly handling errors. Although safeTransferFrom
reverts on failure, adding a require statement would provide explicit error handling and improve readability.function receiveTokens(uint256 tokenAmount) private { require(IERC20(stakeToken).safeTransferFrom(msg.sender, address(this), tokenAmount), "Transfer failed"); }
require(!validatorWhitelistDisabled, "Whitelist is already disabled"); require(_validatorIsAfk(), "Validator is not inactive");
newEdgeChecks
newEdgeChecks
function uses direct if
statements for error handling, leading to inconsistencies.require
statements to improve readability and maintainability.function newEdgeChecks( bytes32 originId, bytes32 startHistoryRoot, uint256 startHeight, bytes32 endHistoryRoot, uint256 endHeight ) internal pure { require(originId != bytes32(0), "EmptyOriginId"); require(startHeight < endHeight, "InvalidHeights"); require(startHistoryRoot != bytes32(0), "EmptyStartRoot"); require(endHistoryRoot != bytes32(0), "EmptyEndRoot"); }
newLayerZeroEdge
and newChildEdge
newLayerZeroEdge
and newChildEdge
functions both call newEdgeChecks
, which checks for redundant conditions already checked in their respective bodies.function newLayerZeroEdge( bytes32 originId, bytes32 startHistoryRoot, uint256 startHeight, bytes32 endHistoryRoot, uint256 endHeight, bytes32 claimId, address staker, uint8 level ) internal view returns (ChallengeEdge memory) { require(staker != address(0), "EmptyStaker"); require(claimId != bytes32(0), "EmptyClaimId"); newEdgeChecks(originId, startHistoryRoot, startHeight, endHistoryRoot, endHeight); return ChallengeEdge({ originId: originId, startHeight: startHeight, startHistoryRoot: startHistoryRoot, endHeight: endHeight, endHistoryRoot: endHistoryRoot, lowerChildId: bytes32(0), upperChildId: bytes32(0), createdAtBlock: uint64(block.number), claimId: claimId, staker: staker, status: EdgeStatus.Pending, level: level, refunded: false, confirmedAtBlock: 0, totalTimeUnrivaledCache: 0 }); }
return ChallengeEdge({ originId: originId, startHeight: startHeight, startHistoryRoot: startHistoryRoot, endHeight: endHeight, endHistoryRoot: endHistoryRoot, lowerChildId: bytes32(0), // No need to explicitly initialize to zero upperChildId: bytes32(0), // No need to explicitly initialize to zero createdAtBlock: uint64(block.number), claimId: claimId, staker: staker, status: EdgeStatus.Pending, level: level, refunded: false, confirmedAtBlock: 0, totalTimeUnrivaledCache: 0 });
mutualId
and id
FunctionsmutualId
and id
functions could be optimizedby using local variables for repeated access of ChallengeEdge
properties.
function mutualId(ChallengeEdge storage ce) internal view returns (bytes32) { uint8 level = ce.level; bytes32 originId = ce.originId; uint256 startHeight = ce.startHeight; bytes32 startHistoryRoot = ce.startHistoryRoot; uint256 endHeight = ce.endHeight; return mutualIdComponent(level, originId, startHeight, startHistoryRoot, endHeight); } function id(ChallengeEdge storage edge) internal view returns (bytes32) { uint8 level = edge.level; bytes32 originId = edge.originId; uint256 startHeight = edge.startHeight; bytes32 startHistoryRoot = edge.startHistoryRoot; uint256 endHeight = edge.endHeight; bytes32 endHistoryRoot = edge.endHistoryRoot; return idComponent(level, originId, startHeight, startHistoryRoot, endHeight, endHistoryRoot); }
EdgeChallengeManagerLib.sol: Lack of Documentation Comments
EdgeChallengeManagerLib
library lack documentation comments explaining their purpose and usage./// @notice Adds a new edge to the store /// @param store The store to add the edge to /// @param edge The edge to add function add(EdgeStore storage store, ChallengeEdge memory edge) internal returns (EdgeAddedData memory) { ... }
EdgeChallengeManagerLib.sol: Unchecked Return Value in bisectEdge
bisectEdge
function does not check the return value of the verifyPrefixProof
function, which could lead to unexpected behavior if the verification fails.verifyPrefixProof
and handle any potential failures appropriately.(bytes32[] memory preExpansion, bytes32[] memory proof) = abi.decode(prefixProof, (bytes32[], bytes32[])); bool proofVerified = MerkleTreeLib.verifyPrefixProof( bisectionHistoryRoot, middleHeight + 1, ce.endHistoryRoot, ce.endHeight + 1, preExpansion, proof ); require(proofVerified, "Prefix proof verification failed");
timeUnrivaledTotal
timeUnrivaledTotal
function could be optimized by caching the store.edges
lookups.function timeUnrivaledTotal(EdgeStore storage store, bytes32 edgeId) internal view returns (uint256) { ChallengeEdge storage edge = store.edges[edgeId]; uint256 totalTimeUnrivaled = timeUnrivaled(store, edgeId); if (edge.lowerChildId != bytes32(0)) { uint256 lowerTimer = store.edges[edge.lowerChildId].totalTimeUnrivaledCache; uint256 upperTimer = store.edges[edge.upperChildId].totalTimeUnrivaledCache; totalTimeUnrivaled += lowerTimer < upperTimer ? lowerTimer : upperTimer; } return totalTimeUnrivaled; }
EdgeChallengeManagerLib
library are inconsistent or unclear, which may make debugging difficult.revert EdgeNotPending(edgeId, store.edges[edgeId].status); // Standardize to: revert("Edge is not pending: invalid status");
setChildren
, setConfirmed
, and setRefunded
functions perform significant state changes without emitting events.event ChildrenSet(bytes32 indexed edgeId, bytes32 lowerChildId, bytes32 upperChildId); event EdgeConfirmed(bytes32 indexed edgeId, uint64 confirmedAtBlock); event Refunded(bytes32 indexed edgeId); function setChildren(ChallengeEdge storage edge, bytes32 lowerChildId, bytes32 upperChildId) internal { if (edge.lowerChildId != 0 || edge.upperChildId != 0) { revert ChildrenAlreadySet(ChallengeEdgeLib.id(edge), edge.lowerChildId, edge.upperChildId); } edge.lowerChildId = lowerChildId; edge.upperChildId = upperChildId; emit ChildrenSet(ChallengeEdgeLib.id(edge), lowerChildId, upperChildId); } function setConfirmed(ChallengeEdge storage edge) internal { if (edge.status != EdgeStatus.Pending) { revert EdgeNotPending(ChallengeEdgeLib.id(edge), edge.status); } edge.status = EdgeStatus.Confirmed; edge.confirmedAtBlock = uint64(block.number); emit EdgeConfirmed(ChallengeEdgeLib.id(edge), edge.confirmedAtBlock); } function setRefunded(ChallengeEdge storage edge) internal { if (edge.status != EdgeStatus.Confirmed) { revert EdgeNotConfirmed(ChallengeEdgeLib.id(edge), edge.status); } if (!isLayerZero(edge)) { revert EdgeNotLayerZero(ChallengeEdgeLib.id(edge), edge.staker, edge.claimId); } if (edge.refunded == true) { revert EdgeAlreadyRefunded(ChallengeEdgeLib.id(edge)); } edge.refunded = true; emit Refunded(ChallengeEdgeLib.id(edge)); }
BridgeCreator.sol: Missing Event Documentation
TemplatesUpdated
and ERC20TemplatesUpdated
events lack documentation describing their purpose.event TemplatesUpdated(uint256 indexed templateId); // This event is emitted when templates are updated
BOLDUpgradeAction.sol: Missing Event Documentation
RollupMigrated
event lacks documentation describing its purpose and emitted parameters.AssertionState.sol: Missing Struct Documentation
AssertionState
struct lacks documentation explaining its purpose and the meaning of its fields.UintUtilsLib.sol: Inconsistent Naming Conventions
leastSignificantBit
and mostSignificantBit
use different naming conventions. It would be more consistent to use `leastSignificantBitand
mostSignificantBitor
lsband
msb`.
- Recommendation: Rename the functions to follow a consistent naming convention.
- Code Snippet:
solidity function leastSignificantBit(uint256 x) internal pure returns (uint256) {...} function mostSignificantBit(uint256 x) internal pure returns (uint256) {...}
UintUtilsLib.sol: Unnecessary Variable Assignment in leastSignificantBit
isolated
variable is assigned the result of ((x - 1) & x) ^ x
, but this value is immediately passed to mostSignificantBit
without being used elsewhere.mostSignificantBit
function call to avoid the unnecessary variable assignment.return mostSignificantBit(((x - 1) & x) ^ x);
MerkleTreeLib.sol: Unclear Error Message in maximumAppendBetween
maximumAppendBetween
function reverts with the error message "Both y and z cannot be zero" when y
and z
are both zero. However, this error message may not provide enough context for the caller to understand the issue.y
and z
.require(y != 0 || z != 0, "Both y and z cannot be zero");
MerkleTreeLib.sol: Inefficient Array Creation in appendCompleteSubTree
appendCompleteSubTree
function creates a new array next
with a length based on the number of most significant bits of postSize
. However, this array creation could be optimized by reusing the existing me
array when possible.me
array when the length remains the same, and only creating a new array when necessary.ChallengeErrors.sol: Inconsistent Naming Convention
ChallengeEdgeLib.sol: Lack of Documentation Comments
ChallengeEdgeLib
library lack documentation comments explaining their purpose and usage./// @notice Common checks to do when adding an edge. /// @param originId The origin id of the edge. /// @param startHistoryRoot The root of all the states in the history up to the startHeight. /// @param startHeight The height of the start history root. /// @param endHistoryRoot The root of all the states in the history up to the endHeight. /// @param endHeight The height of the end history root. function newEdgeChecks( bytes32 originId, bytes32 startHistoryRoot, uint256 startHeight, bytes32 endHistoryRoot, uint256 endHeight ) internal pure { if (originId == 0) { revert EmptyOriginId(); } if (endHeight <= startHeight) { revert InvalidHeights(startHeight, endHeight); } if (startHistoryRoot == 0) { revert EmptyStartRoot(); } if (endHistoryRoot == 0) { revert EmptyEndRoot(); } }
ChallengeEdgeLib.sol: Missing Event Emissions for Significant State Changes
setChildren
, setConfirmed
, and setRefunded
functions perform significant state changes without emitting events.event ChildrenSet(bytes32 indexed edgeId, bytes32 lowerChildId, bytes32 upperChildId); event EdgeConfirmed(bytes32 indexed edgeId, uint64 confirmedAtBlock); event Refunded(bytes32 indexed edgeId); function setChildren(ChallengeEdge storage edge, bytes32 lowerChildId, bytes32 upperChildId) internal { if (edge.lowerChildId != 0 || edge.upperChildId != 0) { revert ChildrenAlreadySet(ChallengeEdgeLib.id(edge), edge.lowerChildId, edge.upperChildId); } edge.lowerChildId = lowerChildId; edge.upperChildId = upperChildId; emit ChildrenSet(ChallengeEdgeLib.id(edge), lowerChildId, upperChildId); } function setConfirmed(ChallengeEdge storage edge) internal { if (edge.status != EdgeStatus.Pending) { revert EdgeNotPending(ChallengeEdgeLib.id(edge), edge.status); } edge.status = EdgeStatus.Confirmed; edge.confirmedAtBlock = uint64(block.number); emit EdgeConfirmed(ChallengeEdgeLib.id(edge), edge.confirmedAtBlock); } function setRefunded(ChallengeEdge storage edge) internal { if (edge.status != EdgeStatus.Confirmed) { revert EdgeNotConfirmed(ChallengeEdgeLib.id(edge), edge.status); } if (!isLayerZero(edge)) { revert EdgeNotLayerZero(ChallengeEdgeLib.id(edge), edge.staker, edge.claimId); } if (edge.refunded == true) { revert EdgeAlreadyRefunded(ChallengeEdgeLib.id(edge)); } edge.refunded = true; emit Refunded(ChallengeEdgeLib.id(edge)); }
uint256
and uint64
TypesChallengeEdge
struct uses both uint256
and uint64
types for similar fields, leading to inconsistency.uint256
or uint64
for height and block number fields for consistency.struct ChallengeEdge { bytes32 originId; bytes32 startHistoryRoot; uint256 startHeight; bytes32 endHistoryRoot; uint256 endHeight; bytes32 lowerChildId; bytes32 upperChildId; bytes32 claimId; address staker; uint64 createdAtBlock; uint64 confirmedAtBlock; EdgeStatus status; uint8 level; bool refunded; uint64 totalTimeUnrivaledCache; }
RollupUserLogic.sol: Inconsistent Usage of uint256
and uint64
Types
deployTimeChainId
is of type uint256
while block numbers are of type uint64
.uint256
or uint64
for similar fields for consistency.uint64 internal immutable deployTimeChainId = uint64(block.chainid);
RollupUserLogic.sol: Missing Event Emissions for Significant State Changes
removeWhitelistAfterFork
and removeWhitelistAfterValidatorAfk
functions perform significant state changes without emitting events.event WhitelistRemoved(); function removeWhitelistAfterFork() external { require(!validatorWhitelistDisabled, "WHITELIST_DISABLED"); require(_chainIdChanged(), "CHAIN_ID_NOT_CHANGED"); validatorWhitelistDisabled = true; emit WhitelistRemoved(); } function removeWhitelistAfterValidatorAfk() external { require(!validatorWhitelistDisabled, "WHITELIST_DISABLED"); require(_validatorIsAfk(), "VALIDATOR_NOT_AFK"); validatorWhitelistDisabled = true; emit WhitelistRemoved(); }
RollupUserLogic.sol: Lack of Documentation Comments
RollupUserLogic
contract lack documentation comments explaining their purpose and usage./// @notice Initializes the RollupUserLogic contract /// @param _stakeToken Address of the staking token function initialize(address _stakeToken) external view override onlyProxy { require(_stakeToken != address(0), "NEED_STAKE_TOKEN"); } /// @notice Removes the validator whitelist after a fork function removeWhitelistAfterFork() external { require(!validatorWhitelistDisabled, "WHITELIST_DISABLED"); require(_chainIdChanged(), "CHAIN_ID_NOT_CHANGED"); validatorWhitelistDisabled = true; } /// @notice Removes the validator whitelist if the validator is inactive function removeWhitelistAfterValidatorAfk() external { require(!validatorWhitelistDisabled, "WHITELIST_DISABLED"); require(_validatorIsAfk(), "VALIDATOR_NOT_AFK"); validatorWhitelistDisabled = true; }
RollupUserLogic.sol: Inconsistent Error Messages
require(!validatorWhitelistDisabled, "Whitelist is already disabled"); require(_validatorIsAfk(), "Validator is not inactive");
safeTransfer
FunctionreceiveTokens
function uses safeTransferFrom
without explicitly handling errors. Although safeTransferFrom
reverts on failure, adding a require statement would provide explicit error handling and improve readability.function receiveTokens(uint256 tokenAmount) private { require(IERC20(stakeToken).safeTransferFrom(msg.sender, address(this), tokenAmount), "Transfer failed"); }
BridgeCreator.sol: Missing Event Documentation
TemplatesUpdated
and ERC20TemplatesUpdated
events lack documentation describing their purpose.event TemplatesUpdated(uint256 indexed templateId); // This event is emitted when templates are updated
BOLDUpgradeAction.sol: Missing Event Documentation
RollupMigrated
event lacks documentation describing its purpose and emitted parameters. This makes it harder to understand the significance and usage of the event./// @notice Emitted when a rollup is migrated to a new rollup address /// @param rollup The address of the expected rollup contract /// @param challengeManager The address of the new challenge manager contract emit RollupMigrated(expectedRollupAddress, address(challengeManager));
/** * @notice Refunds existing stakers, pauses, and upgrades the old rollup to allow stake withdrawals while paused. * @dev This function handles the clean-up process for the old rollup, including pausing it and refunding stakes. */ function cleanupOldRollup() private { IOldRollupAdmin(address(OLD_ROLLUP)).pause(); uint64 stakerCount = ROLLUP_READER.stakerCount(); if (stakerCount > 50) { stakerCount = 50; } for (uint64 i = 0; i < stakerCount; i++) { address stakerAddr = ROLLUP_READER.getStakerAddress(i); OldStaker memory staker = ROLLUP_READER.getStaker(stakerAddr); if (staker.isStaked && staker.currentChallenge == 0) { address[] memory stakersToRefund = new address[](1); stakersToRefund[0] = stakerAddr; IOldRollupAdmin(address(OLD_ROLLUP)).forceRefundStaker(stakersToRefund); } } DoubleLogicUUPSUpgradeable(address(OLD_ROLLUP)).upgradeSecondaryTo(IMPL_PATCHED_OLD_ROLLUP_USER); } /** * @notice Creates the configuration for the new rollup using the latest confirmed assertion from the old rollup. * @return Config The configuration object for the new rollup. * @dev This function fetches the latest confirmed state hash from the old rollup and constructs the genesis state for the new rollup. */ function createConfig() private view returns (Config memory) { bytes32 latestConfirmedStateHash = ROLLUP_READER.getNode(ROLLUP_READER.latestConfirmed()).stateHash; (ExecutionState memory genesisExecState, uint256 inboxMaxCount) = PREIMAGE_LOOKUP.get(latestConfirmedStateHash); AssertionState memory genesisAssertionState; genesisAssertionState.globalState = genesisExecState.globalState; genesisAssertionState.machineStatus = genesisExecState.machineStatus; require( PREIMAGE_LOOKUP.stateHash(genesisAssertionState.toExecutionState(), inboxMaxCount) == latestConfirmedStateHash, "Invalid latest execution hash" ); ISequencerInbox.MaxTimeVariation memory maxTimeVariation; BufferConfig memory bufferConfig; return Config({ confirmPeriodBlocks: CONFIRM_PERIOD_BLOCKS, stakeToken: STAKE_TOKEN, baseStake: STAKE_AMOUNT, wasmModuleRoot: ROLLUP_READER.wasmModuleRoot(), owner: address(this), loserStakeEscrow: L1_TIMELOCK, chainId: CHAIN_ID, chainConfig: "", miniStakeValues: ConstantArrayStorage(MINI_STAKE_AMOUNTS_STORAGE).array(), sequencerInboxMaxTimeVariation: maxTimeVariation, layerZeroBlockEdgeHeight: BLOCK_LEAF_SIZE, layerZeroBigStepEdgeHeight: BIGSTEP_LEAF_SIZE, layerZeroSmallStepEdgeHeight: SMALLSTEP_LEAF_SIZE, genesisAssertionState: genesisAssertionState, genesisInboxCount: inboxMaxCount, anyTrustFastConfirmer: ANY_TRUST_FAST_CONFIRMER, numBigStepLevel: NUM_BIGSTEP_LEVEL, challengeGracePeriodBlocks: CHALLENGE_GRACE_PERIOD_BLOCKS, bufferConfig: bufferConfig }); } /** * @notice Upgrades the surrounding contracts to set the new rollup address. * @param newRollupAddress The address of the new rollup contract. * @dev This function upgrades the bridge, sequencer inbox, outbox, and rollup event inbox to point to the new rollup address. */ function upgradeSurroundingContracts(address newRollupAddress) private { TransparentUpgradeableProxy bridge = TransparentUpgradeableProxy(payable(BRIDGE)); PROXY_ADMIN_BRIDGE.upgrade(bridge, IMPL_BRIDGE); IBridge(BRIDGE).updateRollupAddress(IOwnable(newRollupAddress)); upgradeSequencerInbox(); TransparentUpgradeableProxy inbox = TransparentUpgradeableProxy(payable(INBOX)); PROXY_ADMIN_INBOX.upgrade(inbox, IMPL_INBOX); TransparentUpgradeableProxy rollupEventInbox = TransparentUpgradeableProxy(payable(REI)); PROXY_ADMIN_REI.upgrade(rollupEventInbox, IMPL_REI); IRollupEventInbox(REI).updateRollupAddress(); TransparentUpgradeableProxy outbox = TransparentUpgradeableProxy(payable(OUTBOX)); PROXY_ADMIN_OUTBOX.upgrade(outbox, IMPL_OUTBOX); IOutbox(OUTBOX).updateRollupAddress(); }
#0 - c4-judge
2024-06-05T11:33:14Z
Picodes marked the issue as grade-a
#1 - c4-judge
2024-06-05T11:34:29Z
Picodes marked the issue as grade-c
#2 - c4-judge
2024-06-05T11:34:32Z
Picodes marked the issue as grade-a
#3 - Picodes
2024-06-05T11:51:22Z
This report's formatting is good but reported issues aren't that valuable as they mainly come from static analysis
#4 - CrystallineButterfly
2024-06-07T11:56:54Z
Hello there @Picodes
I would like to respectfully claim this is a QA report, that is worthy of the grade A it was given and is worthy of the top 3 based on:
The following reasons =
I claim the majority are valid and utilising them will better improve the contracts, debugging, documentation and clarity of use.
I claim all can be considered low, I used the umbrella of NC as I was unaware of the recent updates. Which I have noted for next time. Given this I move to claim they are all worthy of the Low label. As low vs non critical is a blurry line.
I do not see any as invalid in my own analysis. All 43 to me I deem valid. But am open to critics with sufficient proof, that would better improve the quality and value of the report next time. And I would also like to add these were not done, from static analysis. For obvious reasons I will not breakdown my process.
My claim here is simply they are valuable with respect to the contracts. And comparatively with the other reports. Which offer great value to. And that given the 43 suggestions, I claim the majority of my suggestions are valid and useful. And therefore the label It has been given is valid, and I see my report as worthy of the top 3 comparatively with the other grade A reports. Which are very good. I do not make any claims in my report that exaggerate or assert false potentials. And stuck to an unbiased process. As best as I could.
All the best - K
#5 - Picodes
2024-06-08T14:55:50Z
@CrystallineButterfly most of the issues are definitely NC. Note that the 3 reports haven't been selected yet and will be chosen following a vote between the judge and the validators
#6 - CrystallineButterfly
2024-06-08T19:22:04Z
Ok thank you @Picodes 🙏