Platform: Code4rena
Start Date: 29/06/2022
Pot Size: $50,000 USDC
Total HM: 20
Participants: 133
Period: 5 days
Judge: hickuphh3
Total Solo HM: 1
Id: 142
League: ETH
Rank: 40/133
Findings: 2
Award: $145.05
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: xiaoming90
Also found by: 0x1f8b, 0x29A, 0x52, 0xDjango, 0xNazgul, 0xNineDec, 0xSolus, 0xf15ers, 0xsanson, AmitN, Bnke0x0, BowTiedWardens, Chom, David_, ElKu, Funen, GalloDaSballo, GimelSec, Hawkeye, IllIllI, JC, JohnSmith, Kaiziron, Kenshin, Lambda, Limbooo, MadWookie, Metatron, MiloTruck, Nethermind, Picodes, ReyAdmirado, Sneakyninja0129, StErMi, TomJ, Treasure-Seeker, TrungOre, Waze, Yiko, _Adam, __141345__, antonttc, async, aysha, catchup, cccz, cryptphi, csanuragjain, danb, datapunk, defsec, delfin454000, dirk_y, doddle0x, durianSausage, exd0tpy, fatherOfBlocks, gogo, hake, hansfriese, horsefacts, hubble, itsmeSTYJ, joestakey, oyc_109, pedroais, peritoflores, rajatbeladiya, reassor, robee, rokinot, samruna, saneryee, sashik_eth, shenwilly, shung, simon135, sseefried, unforgiven, zer0dot, zzzitron
82.1485 USDC - $82.15
The pragma version used are:
pragma solidity 0.8.13;
But recently solidity released a new version with important Bugfixes:
The first one is related to ABI-encoding nested arrays directly from calldata. You can find more information here.
The second bug is triggered in certain inheritance structures and can cause a memory pointer to be interpreted as a calldata pointer or vice-versa. We also have a dedicated blog post about this bug.
Apart from these, there are several minor bug fixes and improvements.
The minimum required version should be 0.8.14
Examples:
getApproved
is deleted when someone transfer a token to himself. By not changing ownership of the token, it is not intended or expected to change approvals.
Affected source code:
It's possible to lose the ownership under specific circumstances.
Because an human error it's possible to set a new invalid owner. When you want to change the owner's address it's better to propose a new owner, and then accept this ownership with the new wallet.
Affected source code:
encode
instead of encodePacked
for hashigUse of abi.encodePacked
in PuttyV2
is safe, but unnecessary and not recommended. abi.encodePacked
can result in hash collisions when used with two dynamic arguments (string/bytes).
There is also discussion of removing abi.encodePacked
from future versions of Solidity (ethereum/solidity#11593), so using abi.encode
now will ensure compatibility in the future.
Affected source code:
It is possible to use the contract as a money launderer since it will be the contract that sends the money from possibly illicit activities to a third account.
Example:
order.isLong
= falseorder.isCall
= falseorder.duration
= 0order.expiration
= type(uint).max
order.maker
= Aliceorder.baseAsset
= WETHorder.premium
= Amount to be laundredorder.whitelist.length
== 0order.strike
= 0fillOrder
with the msg.value = order.premium
Affected source code:
Some contracts use the openzeppelin libraries, as it should be, however these libraries are copied throughout the project instead of use the package manager.
Using the packages from the original developer helps us stay up-to-date when new bugs appear.
Affected source code:
balanceOf
logicThe contract PuttyV2Nft
is abstract and remove the balanceOf
logic in all method except the _burn
one.
Although it is not an issue a priori, if the burn logic is changed to not transfer the token to 0xdead
address, it could cause unnecessary errors.
Reference:
// send the long position to 0xdead. instead of doing a standard burn by sending to 0x000...000, sending to 0xdead ensures that the same position id cannot be minted again.
It's recommended to override the method _burn
like this:
function _burn(uint256 id) internal override { address owner = _ownerOf[id]; require(owner != address(0), "NOT_MINTED"); delete _ownerOf[id]; delete getApproved[id]; emit Transfer(owner, address(0), id); }
Affected source code:
#0 - outdoteth
2022-07-07T19:20:13Z
- Outdated compiler
Duplicate: Use of solidity 0.8.13 with known issues in ABI encoding and memory side effects: https://github.com/code-423n4/2022-06-putty-findings/issues/348
🌟 Selected for report: GalloDaSballo
Also found by: 0v3rf10w, 0x1f8b, 0xA5DF, 0xDjango, 0xHarry, 0xKitsune, 0xNazgul, 0xNineDec, 0xc0ffEE, 0xf15ers, 0xkatana, 0xsanson, ACai, Aymen0909, Bnke0x0, BowTiedWardens, Chom, ElKu, Fitraldys, Funen, Haruxe, Hawkeye, IllIllI, JC, JohnSmith, Kaiziron, Kenshin, Lambda, Limbooo, MadWookie, Metatron, MiloTruck, Picodes, PwnedNoMore, Randyyy, RedOneN, ReyAdmirado, Ruhum, Sm4rty, StErMi, StyxRave, TerrierLover, TomJ, Tomio, UnusualTurtle, Waze, Yiko, _Adam, __141345__, ajtra, ak1, apostle0x01, asutorufos, c3phas, cRat1st0s, catchup, codetilda, cryptphi, datapunk, defsec, delfin454000, durianSausage, exd0tpy, fatherOfBlocks, gogo, grrwahrr, hake, hansfriese, horsefacts, ignacio, jayfromthe13th, joestakey, ladboy233, m_Rassska, mektigboy, minhquanym, mrpathfindr, natzuu, oyc_109, rajatbeladiya, reassor, rfa, robee, rokinot, sach1r0, saian, sashik_eth, simon135, slywaters, swit, z3s, zeesaw, zer0dot
62.9034 USDC - $62.90
Importing pointless files costs gas during deployment and is a bad coding practice that is important to ignore.
Remove the following imports:
Custom errors from Solidity 0.8.4 are cheaper than revert strings (cheaper deployment cost and runtime cost when the revert condition is met)
Source custom Errors in Solidity:
Starting from Solidity v0.8.4, there is a convenient and gas-efficient way to explain to users why an operation failed through the use of custom errors. Until now, you could already use strings to give more information about failures (e.g., revert("Insufficient funds.");), but they are rather expensive, especially when it comes to deploy cost, and it is difficult to use dynamic information in them.
Custom errors are defined using the error statement, which can be used inside and outside of contracts (including interfaces and libraries).
Below the affected contract, it is recommended to change all require
phrases to custom errors.
If you decide not to make the mentioned change, it is recommended to shorten all the messages of the require clauses.
Shortening revert strings to fit in 32 bytes will decrease deployment time gas and will decrease runtime gas when the revert condition is met.
Revert strings that are longer than 32 bytes require at least one additional mstore, along with additional overhead for computing memory offset, etc.
I suggest shortening the revert strings to fit in 32 bytes, or that using custom errors as described before.
external
visibilityThe following methods could be improved if external
visibility is used:
hashOppositeOrder
It's possible to avoid the abi.encode/decode
logic around the method hashOppositeOrder
doing this change:
function hashOppositeOrder(Order memory order) public view returns (bytes32 orderHash) { order.isLong = !order.isLong; orderHash = hashOrder(order); order.isLong = !order.isLong; }
Affected source code:
hashOrder
It's possible to save gas copying all the order fields to abi.encode
changing the method hashOrder
like this:
function hashOrder(Order memory order) public view returns (bytes32 orderHash) { orderHash = keccak256(abi.encode(ORDER_TYPE_HASH, order)); orderHash = _hashTypedDataV4(orderHash); }
Affected source code:
++i
costs less gas compared to i++
or i += 1
++i
costs less gas compared to i++
or i += 1
for unsigned integer, as pre-increment is cheaper (about 5 gas per iteration). This statement is true even with the optimizer enabled.
i++
increments i
and returns the initial value of i
. Which means:
uint i = 1; i++; // == 1 but i == 2
But ++i
returns the actual incremented value:
uint i = 1; ++i; // == 2 and i == 2 too, so no need for a temporary variable
In the first case, the compiler has to create a temporary variable (when used) for returning 1
instead of 2
I suggest using ++i
instead of i++
to increment the value of an uint variable. Same thing for --i
and i--
Affected source code:
If a variable is not set/initialized, the default value is assumed (0, false
, 0x0 ... depending on the data type). You are simply wasting gas if you directly initialize it with its default value.
Affected source code: