Platform: Code4rena
Start Date: 04/01/2022
Pot Size: $25,000 USDC
Total HM: 3
Participants: 40
Period: 3 days
Judge: Ivo Georgiev
Total Solo HM: 1
Id: 75
League: ETH
Rank: 21/40
Findings: 2
Award: $125.78
🌟 Selected for report: 1
🚀 Solo Findings: 0
13.1525 USDC - $13.15
0xsanson
In the relock
function:
// Throw convenient error if trying to re-lock more than was unlocked. `amountUnlocked_ - lockAmount_` would have reverted below anyway. require(lockAmount_ <= amountUnlocked_, "INSUFFICIENT_AMOUNT_UNLOCKED"); // Handle the lock position creation and get the tokenId of the locked position. newTokenId_ = _lock(lockAmount_, duration_, destination_); uint256 withdrawAmount = amountUnlocked_ - lockAmount_;
The last line can use unchecked math since the underflow was already considered before. It saves a bit of gas.
#0 - deluca-mike
2022-01-07T23:59:06Z
Agreed. We originally abstained from unchecked math, for cleaner code, but we will now use unchecked wherever possible.
#1 - deluca-mike
2022-01-09T11:07:02Z
Duplicate #49
🌟 Selected for report: 0xsanson
100.2321 USDC - $100.23
0xsanson
Throughout the code the safe functions safeTransfer
and safeTransferFrom
are used when dealing with XDEFI. Isn't this token a standard ERC20? I believe the normal ERC20 transfer functions can be used. The advantage is gaining some 100s gas otherwise spent in unneeded logic.
grep safeT *.sol
Consider removing the SafeERC20 library.
#0 - deluca-mike
2022-01-06T23:43:49Z
Very true! This is a good one. Good catch!
#1 - deluca-mike
2022-01-14T04:36:53Z
In the released candidate contract, all transfer
s and transferFrom
s are done using the standard IERC20
interface, assuming the XDEFI token contact will revert on a failure.
See the mainnet XDEFI token contract, lines 258 and 259 in the ERC20.sol
contract code.
Specifically, this is now done in:
emergencyUnlock
(which is new)unlock
unlockBatch
_lock
_relock
#2 - Ivshti
2022-01-16T05:50:22Z
good finding!
🌟 Selected for report: agusduha
Also found by: 0xsanson, Czar102, Dravee, GiveMeTestEther, WatchPug, p4st13r4, saian, sirhashalot
4.7941 USDC - $4.79
0xsanson
The state variable MAX_TOTAL_XDEFI_SUPPLY
can be declared constant
to save gas so it won't be read from storage.
uint88 internal /*constant*/ MAX_TOTAL_XDEFI_SUPPLY = uint88(240_000_000_000_000_000_000_000_000);
#0 - deluca-mike
2022-01-07T23:58:31Z
Agreed, it should have been. In any case, due to other changes, we will be removing MAX_TOTAL_XDEFI_SUPPLY
.
#1 - deluca-mike
2022-01-09T11:08:30Z
Duplicate #36
7.6096 USDC - $7.61
0xsanson
The current implementation uses _locked = 0
for the unlocked state and _locked = 1
for the locked one. It's better to have two non-zero values from an optimization point of view; it can surprisingly save thousands of gas in some test cases.
Consider rewriting the modifier to:
modifier noReenter() { require(_locked == 1, "LOCKED"); _locked = uint256(2); _; _locked = uint256(1); }
(also add a _locked = 1
in the constructor).
hardhat gas reporter
#0 - deluca-mike
2022-01-07T23:53:08Z
Agreed. While not always cheaper, it is cheaper in this case for batch unlocks or relocks. Will implement this.
#1 - deluca-mike
2022-01-09T11:10:33Z
Duplicate #1