Platform: Code4rena
Start Date: 04/11/2022
Pot Size: $42,500 USDC
Total HM: 9
Participants: 88
Period: 4 days
Judge: 0xean
Total Solo HM: 2
Id: 180
League: ETH
Rank: 41/88
Findings: 2
Award: $65.42
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0x1f8b
Also found by: 0xSmartContract, 0xc0ffEE, Aymen0909, B2, Deivitto, Josiah, KingNFT, Rahoz, RaymondFam, RedOneN, ReyAdmirado, Trust, ajtra, aviggiano, brgltd, c7e7eff, cryptonue, ctf_sec, delfin454000, djxploit, lukris02, peanuts, rvierdiiev, shark, simon135, slowmoses, tnevler, trustindistrust
44.2869 USDC - $44.29
During the audit, 6 non-critical issues were found.
â„– | Title | Risk Rating | Instance Count |
---|---|---|---|
NC-1 | Order of Functions | Non-Critical | 3 |
NC-2 | Missing NatSpec | Non-Critical | 4 |
NC-3 | Typos | Non-Critical | 4 |
NC-4 | No error message in revert | Non-Critical | 1 |
NC-5 | Unused named return variables | Non-Critical | 2 |
NC-6 | Open question | Non-Critical | 1 |
According to Style Guide, ordering helps readers identify which functions they can call and to find the constructor and fallback definitions easier.
Functions should be grouped according to their visibility and ordered:
public functions before external:
function finalize(uint256 auctionId, uint256[] memory bidIndices, uint128 clearingBase, uint128 clearingQuote)
function tokensAvailableForWithdrawal(uint256 auctionId, uint128 baseAmount)
function computeCommitment(bytes32 message) public pure returns (bytes32)
Reorder functions where possible.
NatSpec is missing for 4 functions in 1 contracts.
function computeCommitment(bytes32 message) public pure returns (bytes32) {
function computeMessage(uint128 baseAmount, bytes16 salt) external pure returns (bytes32) {
function getTimings(uint256 auctionId) external view returns (Timings memory timings) {
function getAuctionData(uint256 auctionId) external view returns (AuctionData memory data) {
Add NatSpec for all functions.
/// @notice Bid on a runnning auction
=> running
/// @notice Finalises an auction by revealing all bids
=> Finalizes
/// @dev Transfers `quoteToken` back to bidder and prevents bid from being finalised
=> finalized
// Prevent any futher access to this EncryptedBid
=> further
revert
Add error messages.
Both named return variable(s) and return statement are used.
To improve clarity use only named return variables.
For example, change:
function functionName() returns (uint id) { return x;
to
function functionName() returns (uint id) { id = x;
#0 - c4-judge
2022-11-10T02:52:41Z
0xean marked the issue as grade-b
🌟 Selected for report: 0x1f8b
Also found by: 0xSmartContract, 0xdeadbeef, Aymen0909, B2, Bnke0x0, Deivitto, Diana, Dinesh11G, JC, RaymondFam, ReyAdmirado, Rolezn, Sathish9098, TomJ, ajtra, aviggiano, chaduke, cryptostellar5, djxploit, gianganhnguyen, gogo, halden, karanctf, leosathya, lukris02, mcwildy, oyc_109, ret2basic, skyle, slowmoses
21.132 USDC - $21.13
During the audit, 4 gas issues were found.
Total savings ~450+.
â„– | Title | Instance Count | Saved |
---|---|---|---|
G-1 | Use calldata instead of memory for read-only arguments | 5 | 300 |
G-2 | Use unchecked blocks for incrementing i | 2 | 70 |
G-3 | Use unchecked blocks for subtractions where underflow is impossible | 2 | 70 |
G-4 | Elements that are smaller than 32 bytes (256 bits) may increase gas usage | 11 |
calldata
instead of memory for
read-only argumentsSince Solidity v0.6.9, memory and calldata are allowed in all functions regardless of their visibility type (See "Calldata Variables" section here).
When function arguments should not be modified, it is cheaper to use calldata.
function finalize(uint256 auctionId, uint256[] memory bidIndices, uint128 clearingBase, uint128 clearingQuote)
function ecMul(Point memory point, uint256 scalar) internal view returns (Point memory) {
function encryptMessage(Point memory encryptToPub, uint256 encryptWithPriv, bytes32 message)
function decryptMessage(Point memory sharedPoint, bytes32 encryptedMessage)
function hashPoint(Point memory point) internal pure returns (bytes32) {
Consider using calldata where possible.
This saves at least 60 gas per iteration.
So, ~60*5 = 300
In Solidity 0.8+, there’s a default overflow and underflow check on unsigned integers. In the loops, "i" will not overflow because the loop will run out of gas before that.
Change:
for (uint256 i; i < n; ++i) { // ... }
to:
for (uint256 i; i < n;) { // ... unchecked { ++i; } }
This saves ~30-40 gas per iteration.
So, ~35*2 = 70
In Solidity 0.8+, there’s a default overflow and underflow check on unsigned integers. When an overflow or underflow isn’t possible (after require or if-statement), some gas can be saved by using unchecked blocks.
This saves ~35.
So, ~35*2 = 70
According to docs, when using elements that are smaller than 32 bytes, your contract’s gas usage may be higher. This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller than that, the EVM must use more operations in order to reduce the size of the element from 32 bytes to the desired size.
uint128 totalBaseAmount;
uint128 filledBase;
uint32 startTimestamp;
uint32 endTimestamp;
uint32 vestingStartTimestamp;
uint32 vestingEndTimestamp;
uint128 cliffPercent;
uint128 lowestBase;
uint128 lowestQuote;
uint128 totalBaseAmount;
uint128 minimumBidQuote;
Consider using a larger size where needed.
#0 - c4-judge
2022-11-10T02:07:29Z
0xean marked the issue as grade-b